6 Commits

Author SHA1 Message Date
felex67 fb9449ba46 mouse event fix 2026-07-12 03:59:21 +05:00
felex67 0b61352d7e comments reformatted 2026-07-12 02:53:52 +05:00
felex67 f861fec8e0 Minimal version finished 2026-06-23 03:25:47 +05:00
felex67 846a79b5a0 finished 2026-06-22 03:31:52 +05:00
felex67 c0ea3a1e23 1. aria-current added in button
2. set(): fixed name -> theme in for(){}
2026-06-20 11:38:25 +05:00
felex67 33729bc897 Minified version added 2026-06-20 03:50:42 +05:00
6 changed files with 371 additions and 2933 deletions
-2864
View File
File diff suppressed because it is too large Load Diff
+110
View File
@@ -0,0 +1,110 @@
/* Settings for dark system theme, same as color-sheme-light */
:root {
--bg-color: #ddd;
--text-color: #222;
--accent-color: #007bff;
--border-color: #aaa;
--shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
--font-family: 'Arial', sans-serif;
--text-shadow: 0 2px 4px rgba(150, 150, 150, 0.3);
--width: 80vw;
--font-size: 1rem;
}
@media (max-width: 756px) {
:root {
--font-size: 1.1rem;
--width: 92vw;
}
}
@media (min-width: 757px) and (max-width: 1024px) {
:root {
--font-size: 1rem;
--width: 90vw;
}
}
/* Settings for dark system theme, same as color-sheme-dark */
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #222;
--text-color: #ddd;
--accent-color: #40a9ff;
--border-color: #444;
--text-shadow: 0 2px 4px rgba(150, 150, 150, 0.3);
--font-family: 'Arial', sans-serif;
}
}
.flxd-tm-color-scheme-dark {
--bg-color: #222;
--text-color: #ddd;
--accent-color: #40a9ff;
--border-color: #444;
--text-shadow: 0 2px 4px rgba(150, 150, 150, 0.3);
--font-family: 'Arial', sans-serif;
}
.flxd-tm-color-scheme-light {
--bg-color: #ddd;
--text-color: #222;
--accent-color: #007bff;
--border-color: #aaa;
--text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
--font-family: 'Arial', sans-serif;
}
body {
margin: auto;
background-color: var(--bg-color);
color: var(--text-color);
text-shadow: var(--text-shadow);
font-family: var(--font-family);
transition: background-color 0.3s ease, color 0.3s ease, font-size 0.3s ease;
width: var(--width);
font-size: var(--font-size);
}
a {
color: var(--text-color);
transition: background-color 0.3s ease, color 0.3s ease;
text-shadow: var(--text-shadow);
font-family: var(--font-family);
font-weight: bold;
transition:transform 0.3s ease, color 0.3s ease;
text-decoration:underline;
display:inline-block;
}
a:hover, a:focus {
transform: scale(1.05);
color:var(--accent-color);
text-shadow: var(--accent-color);
text-decoration: none;
}
.button {
background-color: var(--accent-color);
border: 1px solid var(--border-color);
padding: 0.5rem 1rem;
color: var(--text-color);
border-radius: 4px;
font-family: var(--font-family);
transition: background-color 0.3s ease, color 0.3s ease;
}
#flxd-tm-btn-toggle {
position: fixed;
width: 30px;
height: 30px;
right: 15px;
top: 15px;
border-radius: 18px;
padding: 2px;
filter: drop-shadow(var(--text-shadow));
cursor: pointer;
background-color: rgba(150, 150, 150, 0.3);
transform:rotate(-360deg);
transition:transform 2s ease;
}
#flxd-tm-btn-toggle:hover, #flxd-tm-btn-toggle:focus {
transform:rotate(360deg);
transition:transform 2s ease;
}
+1 -69
View File
@@ -1,70 +1,2 @@
<!DOCTYPE html>
<html lang="ru-RU" dir="ltr">
<head>
<meta name="generator" content="Hugo 0.147.9">
<script src="/livereload.js?mindelay=10&amp;v=2&amp;port=1313&amp;path=livereload" data-no-instant defer></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Подопытный</title>
<link rel="stylesheet" href="css/felexdev-themes.css"> <link rel="stylesheet" href="css/felexdev-themes.css">
<script src="js/felexdev-theme-manager.js"></script> <script src="js/felexdev-theme-manager-minified.js"></script>
</head>
<body>
<header>
<!-- Элемент хранящий в себе векторное изображение которое будет использоваться как содержимое кнопки переключения тем -->
<div id="svg-toggle-theme">
<svg
width="inherit"
height="inherit"
viewBox="0 0 135.46666 135.46667"
version="1.1"
id="svg1"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<path
id="path1"
style="opacity:inherit;fill:currentColor;fill-opacity:1;stroke:#171717;stroke-width:0"
d="M 67.777258,12.777515 A 55,55 0 0 0 12.777515,67.777258 55,55 0 0 0 67.777258,122.77752 55,55 0 0 0 122.77752,67.777258 55,55 0 0 0 67.777258,12.777515 Z m -0.128157,4.95577 V 117.73338 A 50,50 0 0 1 17.733285,67.733333 50,50 0 0 1 67.649101,17.733285 Z" />
</svg>
</div>
<h1>Не бойся что-нибудь сломать, бойся это не исправить! 😂</h1>
<nav>
<ul>
<li>
<a aria-current="page" class="active" href="/">Типа Главная</a>
</li>
<li>
<a href="/posts/">Типа Блог</a>
</li>
</ul>
</nav>
</header>
<main>
<p>Laborum voluptate pariatur ex culpa magna nostrud est incididunt fugiat
pariatur do dolor ipsum enim. Consequat tempor do dolor eu. Non id id anim anim
excepteur excepteur pariatur nostrud qui irure ullamco.</p>
<h2><a href="/posts/post-3/">Лучше не жмакай</a></h2>
<p>Occaecat aliqua consequat laborum ut ex aute aliqua culpa quis irure esse magna dolore quis. Proident fugiat labore eu laboris officia Lorem enim. Ipsum occaecat cillum ut tempor id sint aliqua incididunt nisi incididunt reprehenderit. Voluptate ad minim sint est aute aliquip esse occaecat tempor officia qui sunt. Aute ex ipsum id ut in est velit est laborum incididunt. Aliqua qui id do esse sunt eiusmod id deserunt eu nostrud aute sit ipsum. Deserunt esse cillum Lorem non magna adipisicing mollit amet consequat.</p>
<h2><a href="/posts/post-2/">Сюда тоже</a></h2>
<p>Anim eiusmod irure incididunt sint cupidatat. Incididunt irure irure irure nisi ipsum do ut quis fugiat consectetur proident cupidatat incididunt cillum. Dolore voluptate occaecat qui mollit laborum ullamco et. Ipsum laboris officia anim laboris culpa eiusmod ex magna ex cupidatat anim ipsum aute. Mollit aliquip occaecat qui sunt velit ut cupidatat reprehenderit enim sunt laborum. Velit veniam in officia nulla adipisicing ut duis officia.</p>
<p>Exercitation voluptate irure in irure tempor mollit Lorem nostrud ad officia. Velit id fugiat occaecat do tempor. Sit officia Lorem aliquip eu deserunt consectetur. Aute proident deserunt in nulla aliquip dolore ipsum Lorem ut cupidatat consectetur sit sint laborum. Esse cupidatat sit sint sunt tempor exercitation deserunt. Labore dolor duis laborum est do nisi ut veniam dolor et nostrud nostrud.</p>
<h2><a href="/posts/post-1/">Про эту вообще забудь!</a></h2>
<p>Tempor proident minim aliquip reprehenderit dolor et ad anim Lorem duis sint eiusmod. Labore ut ea duis dolor. Incididunt consectetur proident qui occaecat incididunt do nisi Lorem. Tempor do laborum elit laboris excepteur eiusmod do. Eiusmod nisi excepteur ut amet pariatur adipisicing Lorem.</p>
<p>Occaecat nulla excepteur dolore excepteur duis eiusmod ullamco officia anim in voluptate ea occaecat officia. Cillum sint esse velit ea officia minim fugiat. Elit ea esse id aliquip pariatur cupidatat id duis minim incididunt ea ea. Anim ut duis sunt nisi. Culpa cillum sit voluptate voluptate eiusmod dolor. Enim nisi Lorem ipsum irure est excepteur voluptate eu in enim nisi. Nostrud ipsum Lorem anim sint labore consequat do.</p>
</main>
<footer>
<p>Copyright 2026. All rights reserved. Made with <a href="https://gohugo.io">Hugo</a></p>
</footer>
</body>
</html>
+8
View File
@@ -0,0 +1,8 @@
/**
* @file felexdev-theme-manager.js
* @description Минимальная версия
* @version 0.0.1
* @author felex67 (admin@felexdev.ru)
* @license Apache2.0
*/
class ThemeManager {static #c={bid:'btn-toggle',btid:0,gpfx:'flxd-tm',tpfx:'color-scheme',svg:'<svg width="100%" height="100%" viewBox="0 0 135.46666 135.46667"version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><path id="path1" style="opacity:100%;fill:currentColor;fill-opacity:1;stroke:#171717;stroke-width:0" d="M 67.777258,12.777515 A 55,55 0 0 0 12.777515,67.777258 55,55 0 0 0 67.777258,122.77752 55,55 0 0 0 122.77752,67.777258 55,55 0 0 0 67.777258,12.777515 Z m -0.128157,4.95577 V 117.73338 A 50,50 0 0 1 17.733285,67.733333 50,50 0 0 1 67.649101,17.733285 Z"/></svg>',mrqd:window.matchMedia('(prefers-color-scheme: dark)'),rqdh:null};static #cl=document.documentElement.classList;static #b=document.createElement('div');static #i=0;static #t=[];static toggle=()=>{this.#s((this.#i+1)%this.#t.length);};static #bclk=(e)=>{if(e.type==='click'||e.key==='Enter'||e.key===' ')this.#s((this.#i+1)%2)};static #stch=(e)=>{if(e.matches!=this.#i)this.#s(e.matches+0);};static{let c=this.#c;let b=this.#b;c.gpfx=c.gpfx+'-';c.tpfx=`${c.gpfx}${c.tpfx}-`;b.id=c.bid=c.gpfx+c.bid;b.tabIndex=c.btid;b.innerHTML=c.svg;b.setAttribute('aria-label', 'toggle color scheme');b.addEventListener('click',this.#bclk);b.addEventListener('keydown',this.#bclk);c.rqdh=c.mrqd.addEventListener('change',this.#stch);};static #at=(n)=>{this.#t.push({n:n,s:this.#c.tpfx+n});};static #s(i){if(i==this.#i)return;let m=this;m.#cl.replace(m.#t[m.#i].s,m.#t[i].s);m.#sv(m.#i=i);m.#b.setAttribute('aria-current',m.#t[i].n);};static #sv=(()=>{let s=()=>{console.error(this.#c.gpfx+'#sv: Невозможно сохранить тему, хранилище недоступно.');};try{this.#i=parseInt(localStorage.getItem(this.#c.tpfx))||0;s=()=>{try{localStorage.setItem(this.#c.tpfx,this.#i);}catch(e){console.error(e);}};}catch(e){console.error(this.#c.gpfx+'#sv-IIFE: localStorage не доступен.');}return s;})();static{let i=this.#i;let t=this.#t;this.#at('light');this.#at('dark');if(i>=t.length||i<0){console.error(`${this.#c.gpfx}bottom-static: В хранилище сохранён не валидный индекс '${i}'. Сброс на системную тему`);this.#i=this.#c.mrqd.matches+0;}this.#cl.add(t[i].s);this.#b.setAttribute('aria-current',t[i].n);};static set=(name)=>{let i=-1;for(let j=0;j<this.#t.length;j++){if(this.#t[j].n===name){i=j;break;}}if(i==-1){throw new Error(`Темы '${name}' не существует!.`);}this.#s(i);};static #_=()=>{window.removeEventListener('DOMContentLoaded',this.#_);document.body.appendChild(this.#b);};static {if(!document.body)window.addEventListener('DOMContentLoaded',this.#_);};};
+199
View File
@@ -0,0 +1,199 @@
/**
* @file felexdev-theme-manager.js
* @description Минимальная версия
* @version 0.0.1
* @author felex67 (admin@felexdev.ru)
* @license Apache2.0
*/
class ThemeManager {
/**
* @typedef {Object} Theme
*
* @property {string} name - Внутреннее название темы.
* @property {string} selector - CSS-селектор темы. Без точки.
*/
/**
* Конфигурация модулю
* @typedef {Object} Config
*
* @property {string} btnId - CSS-id-селектор кнопки.
* @property {number} btnTabIndex - Таб-индекс кнопки.
* @property {string} globalPrefix - Глобальный префикс.
* @property {string} themePrefix - Префикс CSS-class-селектора темы.
* @property {string} svg - PlainSVG-иконка темы.
*/
static #config = {
btnId: 'btn-toggle', // Идентификатор кнопки переключения: `gpfx + btnid == 'flxd-tm-btn-toggle'`
btnTabIndex: 0, // Индекс табуляции кнопки переключения тем
globalPrefix: 'flxd-tm', // Глобальный префикс: `gpfx + '-' == 'flxd-tm-'`
themePrefix: 'color-scheme', // Селектор CSS: `gpfx + stdid == 'flxd-tm-color-scheme'`
svg: '<svg width="100%" height="100%" viewBox="0 0 135.46666 135.46667"version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><path id="path1" style="opacity:100%;fill:currentColor;fill-opacity:1;stroke:#171717;stroke-width:0" d="M 67.777258,12.777515 A 55,55 0 0 0 12.777515,67.777258 55,55 0 0 0 67.777258,122.77752 55,55 0 0 0 122.77752,67.777258 55,55 0 0 0 67.777258,12.777515 Z m -0.128157,4.95577 V 117.73338 A 50,50 0 0 1 17.733285,67.733333 50,50 0 0 1 67.649101,17.733285 Z"/></svg>',
mediaRequestDark: window.matchMedia('(prefers-color-scheme: dark)'),
requestDarkHandle: null,
};
/**
* Список классов корневого элемента
*
* @static
* @type {HTMLTokenList}
*/
static #classList = document.documentElement.classList;
/**
* Кнопка переключения тем
*
* @static
* @type {HTMLDivElement}
*/
static #button = document.createElement('div');
/**
* Идентификатор текущей темы
*
* @static
* @type {number}
*/
static #index = 0;
/**
* Массив тем
*
* @static
* @type {[Theme]}
*/
static #themes = [];
/**
* Переключает тему
*/
static toggle = () => {
this.#set((this.#index + 1) % this.#themes.length);
};
/**
* Обработчик события нажатия кнопки переключения темы
*/
static #btnClick = (e) => {
if (e.type === 'click' || e.key === 'Enter' || e.key === ' ')
this.#set((this.#index + 1) % 2)
};
/**
* Обработчик события изменения системной темы
*
* @param {Event} e
*/
static #sysThemeChange = (e) => {
if (e.matches != this.#index)
this.#set(e.matches + 0);
};
static { /* Инициализация конфигурации */
const config = this.#config;
const button = this.#button;
/* Настраиваем префиксы */
config.globalPrefix = config.globalPrefix + '-';
config.themePrefix = `${config.globalPrefix}${config.themePrefix}-`;
/* Настраиваем кнопку */
button.id = config.btnId = config.globalPrefix + config.btnId;
button.tabIndex = config.btnTabIndex;
button.innerHTML = config.svg;
/* Настраиваем обработчики */
button.setAttribute('aria-label', 'toggle color scheme');
button.addEventListener('click', this.#btnClick);
button.addEventListener('keydown', this.#btnClick);
/* Устанавливаем обработчик смены системной темы */
config.requestDarkHandle = config.mediaRequestDark.addEventListener('change', this.#sysThemeChange);
};
/**
* Добавить новую тему
* @param {string} name - Название темы используемое в селекторе CSS. 'light' -> 'flxd-tm-color-scheme-light'
*/
static #addTheme = (name) => {
const theme = {
name: name,
selector: this.#config.themePrefix + name,
};
this.#themes.push(theme);
};
/**
* Устанавливаем тему по индексу
* @param {number} index
*/
static #set(index) {
if (index == this.#index)
return;
this.#classList.replace(this.#themes[this.#index].selector, this.#themes[index].selector);
this.#save(this.#index = index);
this.#button.setAttribute('aria-current', this.#themes[index].name);
};
/**
* Сохраняет индекс текущей темы
* @type {function() => void}
* @returns void
*/
static #save = (() => {
let save = () => { console.error(this.#config.globalPrefix + '#save: Невозможно сохранить тему, хранилище недоступно.'); };
try {
this.#index = parseInt(localStorage.getItem(this.#config.themePrefix)) || 0;
save = () => {
try { localStorage.setItem(this.#config.themePrefix, this.#index); }
catch(e) { console.error(e); }
};
}
catch(e) {
console.error(this.#config.globalPrefix + '#sv-IIFE: localStorage не доступен.');
}
return save;
})();
static { /* Инициализация тем */
let index = this.#index;
let themes = this.#themes;
/* Добавляем темы(светлая/темная) */
this.#addTheme('light');
this.#addTheme('dark');
/* Проверяем индекс */
if (index >= themes.length || index < 0) {
console.error(`${this.#config.globalPrefix}bottom-static: В хранилище сохранён не валидный индекс '${index}'. Сброс на системную тему`);
this.#index = this.#config.mediaRequestDark.matches + 0;
}
/* Применяем тему */
this.#classList.add(themes[index].selector);
this.#button.setAttribute('aria-current', themes[index].name);
};
/**
*
* @param {string} name Название темы
* @throws Error если `name` нет в списке
*/
static set = (name) => {
let i = -1;
for(let j = 0; j < this.#themes.length; j++) {
if(this.#themes[j].name === name) {
i = j;
break;
}
}
if(i == -1) {
throw new Error(`Темы '${name}' не существует!.`);
}
this.#set(i);
};
/**
* Инициализация интерфейса. Добавляет кнопку к документу
*/
static #_ = () => {
window.removeEventListener('DOMContentLoaded',this.#_);
document.body.appendChild(this.#button);
};
/** Назначаем обработчик события создания DOM */
static {
this.#set(this.#index);
if (!document.body)window.addEventListener('DOMContentLoaded', this.#_);
}
};
+53
View File
@@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="ru-RU" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Подопытный</title>
<link rel="stylesheet" href="css/felexdev-themes.css">
<script src="js/felexdev-theme-manager.js"></script>
</head>
<body>
<header>
<h1>Не бойся что-нибудь сломать, бойся это не исправить! 😂</h1>
<nav>
<ul>
<li>
<a aria-current="page" class="active" href="/">Типа Главная</a>
</li>
<li>
<a href="/posts/">Типа Блог</a>
</li>
</ul>
</nav>
</header>
<main>
<p>Laborum voluptate pariatur ex culpa magna nostrud est incididunt fugiat
pariatur do dolor ipsum enim. Consequat tempor do dolor eu. Non id id anim anim
excepteur excepteur pariatur nostrud qui irure ullamco.</p>
<h2><a href="/posts/post-3/">Лучше не жмакай</a></h2>
<p>Occaecat aliqua consequat laborum ut ex aute aliqua culpa quis irure esse magna dolore quis. Proident fugiat labore eu laboris officia Lorem enim. Ipsum occaecat cillum ut tempor id sint aliqua incididunt nisi incididunt reprehenderit. Voluptate ad minim sint est aute aliquip esse occaecat tempor officia qui sunt. Aute ex ipsum id ut in est velit est laborum incididunt. Aliqua qui id do esse sunt eiusmod id deserunt eu nostrud aute sit ipsum. Deserunt esse cillum Lorem non magna adipisicing mollit amet consequat.</p>
<h2><a href="/posts/post-2/">Сюда тоже</a></h2>
<p>Anim eiusmod irure incididunt sint cupidatat. Incididunt irure irure irure nisi ipsum do ut quis fugiat consectetur proident cupidatat incididunt cillum. Dolore voluptate occaecat qui mollit laborum ullamco et. Ipsum laboris officia anim laboris culpa eiusmod ex magna ex cupidatat anim ipsum aute. Mollit aliquip occaecat qui sunt velit ut cupidatat reprehenderit enim sunt laborum. Velit veniam in officia nulla adipisicing ut duis officia.</p>
<p>Exercitation voluptate irure in irure tempor mollit Lorem nostrud ad officia. Velit id fugiat occaecat do tempor. Sit officia Lorem aliquip eu deserunt consectetur. Aute proident deserunt in nulla aliquip dolore ipsum Lorem ut cupidatat consectetur sit sint laborum. Esse cupidatat sit sint sunt tempor exercitation deserunt. Labore dolor duis laborum est do nisi ut veniam dolor et nostrud nostrud.</p>
<h2><a href="/posts/post-1/">Про эту вообще забудь!</a></h2>
<p>Tempor proident minim aliquip reprehenderit dolor et ad anim Lorem duis sint eiusmod. Labore ut ea duis dolor. Incididunt consectetur proident qui occaecat incididunt do nisi Lorem. Tempor do laborum elit laboris excepteur eiusmod do. Eiusmod nisi excepteur ut amet pariatur adipisicing Lorem.</p>
<p>Occaecat nulla excepteur dolore excepteur duis eiusmod ullamco officia anim in voluptate ea occaecat officia. Cillum sint esse velit ea officia minim fugiat. Elit ea esse id aliquip pariatur cupidatat id duis minim incididunt ea ea. Anim ut duis sunt nisi. Culpa cillum sit voluptate voluptate eiusmod dolor. Enim nisi Lorem ipsum irure est excepteur voluptate eu in enim nisi. Nostrud ipsum Lorem anim sint labore consequat do.</p>
</main>
<footer>
<p>Copyright 2026. All rights reserved. Made with <a href="https://gohugo.io">Hugo</a></p>
</footer>
</body>
</html>