feat(dev): add font base and ratio controls to theme adapter panel
Adds a "Font" section with two sliders:
- Base (0.75–1.25 rem) — the root font size
- Ratio (1.05–1.50) — the geometric scale factor
Live-updates all --font-{xs,sm,base,md,lg,xl,2xl,3xl} variables.
Persists in localStorage, resets with Reset, and is included in
the Copy EDN output.
This commit is contained in:
@@ -49,9 +49,15 @@
|
|||||||
var SIZE_STEPS = 16; // --size-1 through --size-16
|
var SIZE_STEPS = 16; // --size-1 through --size-16
|
||||||
var DEFAULT_SIZE_BASE = 0.25; // rem
|
var DEFAULT_SIZE_BASE = 0.25; // rem
|
||||||
|
|
||||||
|
// ── Font scale ────────────────────────────────────────────────
|
||||||
|
// Geometric: --font-{label} = base × ratio^power
|
||||||
|
var FONT_STEPS = [[-2,'xs'],[-1,'sm'],[0,'base'],[1,'md'],[2,'lg'],[3,'xl'],[4,'2xl'],[5,'3xl']];
|
||||||
|
var DEFAULT_FONT_BASE = 1.0; // rem
|
||||||
|
var DEFAULT_FONT_RATIO = 1.25;
|
||||||
|
|
||||||
// ── State ──────────────────────────────────────────────────────
|
// ── State ──────────────────────────────────────────────────────
|
||||||
var STORAGE_KEY = 'ui-fw-theme-adapter-v2';
|
var STORAGE_KEY = 'ui-fw-theme-adapter-v2';
|
||||||
var DEFAULT = { grayHue: 285, graySat: 1.0, accentHue: 286, accentSat: 1.0, sizeBase: DEFAULT_SIZE_BASE, open: false };
|
var DEFAULT = { grayHue: 285, graySat: 1.0, accentHue: 286, accentSat: 1.0, sizeBase: DEFAULT_SIZE_BASE, fontBase: DEFAULT_FONT_BASE, fontRatio: DEFAULT_FONT_RATIO, open: false };
|
||||||
var state = assign({}, DEFAULT);
|
var state = assign({}, DEFAULT);
|
||||||
try {
|
try {
|
||||||
var saved = JSON.parse(localStorage.getItem(STORAGE_KEY));
|
var saved = JSON.parse(localStorage.getItem(STORAGE_KEY));
|
||||||
@@ -90,10 +96,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyFont() {
|
||||||
|
var root = document.documentElement.style;
|
||||||
|
for (var i = 0; i < FONT_STEPS.length; i++) {
|
||||||
|
var power = FONT_STEPS[i][0], label = FONT_STEPS[i][1];
|
||||||
|
var val = state.fontBase * Math.pow(state.fontRatio, power);
|
||||||
|
var s = val.toFixed(3).replace(/0+$/, '').replace(/\.$/, '');
|
||||||
|
root.setProperty('--font-' + label, s + 'rem');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function apply() {
|
function apply() {
|
||||||
applyScale('gray', state.grayHue, GRAY_STEPS, state.graySat);
|
applyScale('gray', state.grayHue, GRAY_STEPS, state.graySat);
|
||||||
applyScale('accent', state.accentHue, ACCENT_STEPS, state.accentSat);
|
applyScale('accent', state.accentHue, ACCENT_STEPS, state.accentSat);
|
||||||
applySpacing();
|
applySpacing();
|
||||||
|
applyFont();
|
||||||
save();
|
save();
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
@@ -104,6 +121,7 @@
|
|||||||
GRAY_STEPS.forEach(function(s) { root.removeProperty('--gray-' + s[0]); });
|
GRAY_STEPS.forEach(function(s) { root.removeProperty('--gray-' + s[0]); });
|
||||||
ACCENT_STEPS.forEach(function(s) { root.removeProperty('--accent-' + s[0]); });
|
ACCENT_STEPS.forEach(function(s) { root.removeProperty('--accent-' + s[0]); });
|
||||||
for (var n = 1; n <= SIZE_STEPS; n++) { root.removeProperty('--size-' + n); }
|
for (var n = 1; n <= SIZE_STEPS; n++) { root.removeProperty('--size-' + n); }
|
||||||
|
for (var i = 0; i < FONT_STEPS.length; i++) { root.removeProperty('--font-' + FONT_STEPS[i][1]); }
|
||||||
save();
|
save();
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
@@ -119,8 +137,15 @@
|
|||||||
var grayChroma = Math.min(0.4, 0.025 * state.graySat);
|
var grayChroma = Math.min(0.4, 0.025 * state.graySat);
|
||||||
var accentChroma = Math.min(0.4, 0.23 * state.accentSat);
|
var accentChroma = Math.min(0.4, 0.23 * state.accentSat);
|
||||||
var sizeBase = state.sizeBase.toFixed(3).replace(/0+$/, '').replace(/\.$/, '');
|
var sizeBase = state.sizeBase.toFixed(3).replace(/0+$/, '').replace(/\.$/, '');
|
||||||
|
var fontBase = state.fontBase.toFixed(3).replace(/0+$/, '').replace(/\.$/, '');
|
||||||
|
var fontRatio = state.fontRatio.toFixed(3).replace(/0+$/, '').replace(/\.$/, '');
|
||||||
|
var fontStepsEDN = FONT_STEPS.map(function(s) {
|
||||||
|
return '[' + String(s[0]).padStart(2) + ' "' + s[1] + '"]';
|
||||||
|
}).join('\n ');
|
||||||
return ':scales\n' +
|
return ':scales\n' +
|
||||||
' {:size {:base ' + sizeBase + ' :unit "rem" :steps ' + SIZE_STEPS + '}\n\n' +
|
' {:size {:base ' + sizeBase + ' :unit "rem" :steps ' + SIZE_STEPS + '}\n\n' +
|
||||||
|
' :font {:base ' + fontBase + ' :unit "rem" :ratio ' + fontRatio + '\n' +
|
||||||
|
' :steps [' + fontStepsEDN + ']}\n\n' +
|
||||||
' :color\n' +
|
' :color\n' +
|
||||||
' {:gray {:hue ' + state.grayHue + ' :chroma ' + grayChroma.toFixed(3) + '\n' +
|
' {:gray {:hue ' + state.grayHue + ' :chroma ' + grayChroma.toFixed(3) + '\n' +
|
||||||
' :steps [' + fmtSteps(GRAY_STEPS, state.graySat).trimStart() + ']}\n\n' +
|
' :steps [' + fmtSteps(GRAY_STEPS, state.graySat).trimStart() + ']}\n\n' +
|
||||||
@@ -258,10 +283,17 @@
|
|||||||
spacingSection.appendChild(el('div', 'text-xs text-faint uppercase tracking-wide font-semibold', 'Spacing'));
|
spacingSection.appendChild(el('div', 'text-xs text-faint uppercase tracking-wide font-semibold', 'Spacing'));
|
||||||
spacingSection.appendChild(makeSlider('sizeBase', 'Base', 10, 50, 1));
|
spacingSection.appendChild(makeSlider('sizeBase', 'Base', 10, 50, 1));
|
||||||
|
|
||||||
|
// Font section
|
||||||
|
var fontSection = el('div', 'vstack gap-2');
|
||||||
|
fontSection.appendChild(el('div', 'text-xs text-faint uppercase tracking-wide font-semibold', 'Font'));
|
||||||
|
fontSection.appendChild(makeSlider('fontBase', 'Base', 75, 125, 1));
|
||||||
|
fontSection.appendChild(makeSlider('fontRatio', 'Ratio', 105, 150, 1));
|
||||||
|
|
||||||
body.appendChild(presetRow);
|
body.appendChild(presetRow);
|
||||||
body.appendChild(graySection);
|
body.appendChild(graySection);
|
||||||
body.appendChild(accentSection);
|
body.appendChild(accentSection);
|
||||||
body.appendChild(spacingSection);
|
body.appendChild(spacingSection);
|
||||||
|
body.appendChild(fontSection);
|
||||||
|
|
||||||
// ─ Footer section ─
|
// ─ Footer section ─
|
||||||
var footer = el('div', 'card-section');
|
var footer = el('div', 'card-section');
|
||||||
@@ -299,6 +331,12 @@
|
|||||||
inputs.sizeBase.input.addEventListener('input', function(e) {
|
inputs.sizeBase.input.addEventListener('input', function(e) {
|
||||||
state.sizeBase = parseInt(e.target.value) / 100; apply();
|
state.sizeBase = parseInt(e.target.value) / 100; apply();
|
||||||
});
|
});
|
||||||
|
inputs.fontBase.input.addEventListener('input', function(e) {
|
||||||
|
state.fontBase = parseInt(e.target.value) / 100; apply();
|
||||||
|
});
|
||||||
|
inputs.fontRatio.input.addEventListener('input', function(e) {
|
||||||
|
state.fontRatio = parseInt(e.target.value) / 100; apply();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Update UI ─────────────────────────────────────────────────
|
// ── Update UI ─────────────────────────────────────────────────
|
||||||
@@ -325,6 +363,12 @@
|
|||||||
inputs.sizeBase.input.value = Math.round(state.sizeBase * 100);
|
inputs.sizeBase.input.value = Math.round(state.sizeBase * 100);
|
||||||
inputs.sizeBase.value.textContent = state.sizeBase.toFixed(2);
|
inputs.sizeBase.value.textContent = state.sizeBase.toFixed(2);
|
||||||
|
|
||||||
|
// Sync font sliders
|
||||||
|
inputs.fontBase.input.value = Math.round(state.fontBase * 100);
|
||||||
|
inputs.fontBase.value.textContent = state.fontBase.toFixed(2);
|
||||||
|
inputs.fontRatio.input.value = Math.round(state.fontRatio * 100);
|
||||||
|
inputs.fontRatio.value.textContent = state.fontRatio.toFixed(2);
|
||||||
|
|
||||||
// Preset active states — uses .chip / .chip-active
|
// Preset active states — uses .chip / .chip-active
|
||||||
presetBtns.forEach(function(item) {
|
presetBtns.forEach(function(item) {
|
||||||
var p = item.preset;
|
var p = item.preset;
|
||||||
@@ -353,7 +397,8 @@
|
|||||||
buildPanel();
|
buildPanel();
|
||||||
if (state.grayHue !== DEFAULT.grayHue || state.graySat !== DEFAULT.graySat ||
|
if (state.grayHue !== DEFAULT.grayHue || state.graySat !== DEFAULT.graySat ||
|
||||||
state.accentHue !== DEFAULT.accentHue || state.accentSat !== DEFAULT.accentSat ||
|
state.accentHue !== DEFAULT.accentHue || state.accentSat !== DEFAULT.accentSat ||
|
||||||
state.sizeBase !== DEFAULT.sizeBase) {
|
state.sizeBase !== DEFAULT.sizeBase ||
|
||||||
|
state.fontBase !== DEFAULT.fontBase || state.fontRatio !== DEFAULT.fontRatio) {
|
||||||
apply();
|
apply();
|
||||||
} else {
|
} else {
|
||||||
updateUI();
|
updateUI();
|
||||||
|
|||||||
Reference in New Issue
Block a user