web: move the section jumps to their own strip and grow the Theme menu
The six section links leave the top bar for a thin shelf under it, each a bordered pill at the page's normal text size, so the bar itself stays a row of actions and the jumps still read as buttons. The Theme menu gains the workspace's colour groups — the landing palette now takes its hue from the accent tokens — an Auto mode that follows the OS scheme live, and a dropdown in place of the three font chips.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { api, type Photo, type PhotoSlot, type User } from './api';
|
||||
import { useI18n } from './i18n/I18nProvider';
|
||||
import { FONT_SETS, useTheme } from './theme/ThemeProvider';
|
||||
import { ACCENTS, FONT_SETS, useTheme, type FontSetId } from './theme/ThemeProvider';
|
||||
import { AuthModal } from './ui/AuthModal';
|
||||
import { Avatar } from './ui/Avatar';
|
||||
|
||||
@@ -102,7 +102,7 @@ function Icon({ name }: { name: Feature['icon'] }) {
|
||||
|
||||
export function Landing() {
|
||||
const { t, lang, setLang } = useI18n();
|
||||
const { mode, fonts, setMode, setFonts } = useTheme();
|
||||
const { mode, accent, fonts, setMode, setAccent, setFonts } = useTheme();
|
||||
const [menu, setMenu] = useState(false);
|
||||
const [themeMenu, setThemeMenu] = useState(false);
|
||||
const [accountMenu, setAccountMenu] = useState(false);
|
||||
@@ -279,9 +279,6 @@ export function Landing() {
|
||||
text node + <em> would get the 6px gap between them. */}
|
||||
<span className="lp-logo-word">Recipes<em>Cam</em></span>
|
||||
</a>
|
||||
<div className="lp-nav-links">
|
||||
{nav.map((entry) => <a key={entry.href} href={entry.href}>{c(entry.label)}</a>)}
|
||||
</div>
|
||||
<span className="lp-spacer" />
|
||||
{/* The browser studio is live today, so it leads the row and wears
|
||||
the same primary pill as the store CTA. */}
|
||||
@@ -323,21 +320,41 @@ export function Landing() {
|
||||
>
|
||||
Dark
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`chip${mode === 'auto' ? ' on' : ''}`}
|
||||
data-key="theme-auto"
|
||||
onClick={() => setMode('auto')}
|
||||
>
|
||||
Auto
|
||||
</button>
|
||||
</div>
|
||||
{/* The same colour groups the workspace offers; the page's own
|
||||
amber/red palette follows the chosen hue. */}
|
||||
<div className="chip-row" style={{ marginTop: 8 }}>
|
||||
{FONT_SETS.map((entry) => (
|
||||
{ACCENTS.map((entry) => (
|
||||
<button
|
||||
key={entry.id}
|
||||
type="button"
|
||||
data-key={`fonts-${entry.id}`}
|
||||
title={entry.note}
|
||||
className={`chip${fonts === entry.id ? ' on' : ''}`}
|
||||
onClick={() => setFonts(entry.id)}
|
||||
data-key={`accent-${entry.id}`}
|
||||
className={`chip${accent === entry.id ? ' on' : ''}`}
|
||||
onClick={() => setAccent(entry.id)}
|
||||
>
|
||||
{entry.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<select
|
||||
className="chip font-select"
|
||||
data-key="fonts"
|
||||
title={FONT_SETS.find((f) => f.id === fonts)?.note}
|
||||
value={fonts}
|
||||
onChange={(e) => setFonts(e.target.value as FontSetId)}
|
||||
>
|
||||
{FONT_SETS.map((entry) => (
|
||||
<option key={entry.id} value={entry.id} title={entry.note}>{entry.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -391,6 +408,16 @@ export function Landing() {
|
||||
<i /><i /><i />
|
||||
</button>
|
||||
</div>
|
||||
{/* The six section jumps live on their own shelf under the bar: the
|
||||
top row stays a row of actions, and each jump wears a border so it
|
||||
reads as a button rather than a word in a menu. */}
|
||||
<div className="lp-subnav" data-key="subnav">
|
||||
<div className="lp-wrap">
|
||||
{nav.map((entry) => (
|
||||
<a key={entry.href} className="lp-subnav-link" href={entry.href}>{c(entry.label)}</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{menu ? (
|
||||
<div className="lp-sheet">
|
||||
{nav.map((entry) => <a key={entry.href} href={entry.href} onClick={() => setMenu(false)}>{c(entry.label)}</a>)}
|
||||
|
||||
@@ -119,6 +119,19 @@
|
||||
.chip .val { color: var(--text-dim); font-family: var(--mono); font-size: 11px; }
|
||||
.chip .pro { font-size: 9px; letter-spacing: 0.08em; color: var(--accent); }
|
||||
.chip:disabled { opacity: 0.4; cursor: not-allowed; }
|
||||
/* The font group as a dropdown: one row that shows the current pairing instead
|
||||
of three chips that all have to fit. */
|
||||
.font-select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
padding-right: 26px;
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%239aa2ad' stroke-width='1.5'/%3E%3C/svg%3E");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 10px center;
|
||||
}
|
||||
.font-select option { background: var(--bg-elev); color: var(--text); }
|
||||
|
||||
/* The ruler column: "<" back button, label + live value, slider, colour swatch
|
||||
for the knobs that drive a hue (WB's TEMPERATURE / TINT). */
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
--lp-bg: #090a0f;
|
||||
--lp-bg2: #0d0e12;
|
||||
--lp-card: #161820;
|
||||
--lp-amber: #fbbf24;
|
||||
--lp-orange: #f59e0b;
|
||||
/* The colour group in the Theme menu retints the page: the workspace already
|
||||
resolves the accent hue, so the blueprint's amber/orange pair just borrows
|
||||
it. --lp-red stays the film red, the one fixed point of the palette. */
|
||||
--lp-amber: var(--accent);
|
||||
--lp-orange: var(--accent-hover);
|
||||
--lp-red: #ff5e36;
|
||||
--lp-cyan: #38bdf8;
|
||||
--lp-indigo: #818cf8;
|
||||
@@ -42,7 +45,7 @@
|
||||
--lp-muted: #5b6472;
|
||||
--lp-line: rgba(20, 22, 28, 0.12);
|
||||
--lp-nav: rgba(247, 247, 248, 0.72);
|
||||
--lp-ink-amber: #a16207;
|
||||
--lp-ink-amber: hsl(var(--accent-h) var(--accent-s) 32%);
|
||||
}
|
||||
[data-theme="light"] .lp-hero h1 { background: linear-gradient(100deg, #14161c 8%, var(--lp-amber) 62%, var(--lp-red) 98%); -webkit-background-clip: text; background-clip: text; }
|
||||
[data-theme="light"] .lp-glow { opacity: 0.5; }
|
||||
@@ -54,10 +57,10 @@
|
||||
[data-theme="light"] .lp-frame { background: #ffffff; }
|
||||
[data-theme="light"] .lp-perf i { background: rgba(20, 22, 28, 0.55); box-shadow: none; }
|
||||
[data-theme="light"] .lp-recipe-card { background: linear-gradient(160deg, #ffffff, #eef0f4); box-shadow: 0 24px 60px rgba(20, 22, 28, 0.18); }
|
||||
[data-theme="light"] .lp-plan.vip { background: linear-gradient(180deg, rgba(245, 158, 11, 0.14), rgba(255, 255, 255, 0.92)); }
|
||||
[data-theme="light"] .lp-plan.vip { background: linear-gradient(180deg, var(--accent-soft), rgba(255, 255, 255, 0.92)); }
|
||||
[data-theme="light"] .lp-toast { background: rgba(255, 255, 255, 0.98); box-shadow: 0 20px 50px rgba(20, 22, 28, 0.22); }
|
||||
|
||||
html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
html { scroll-behavior: smooth; scroll-padding-top: 116px; }
|
||||
.lp ::selection { background: var(--lp-amber); color: #10121a; }
|
||||
.lp-wrap { max-width: 1180px; margin: 0 auto; padding: 0 22px; }
|
||||
.lp h1, .lp h2, .lp h3 { font-family: var(--lp-display); font-weight: 800; line-height: 1.1; margin: 0; }
|
||||
@@ -93,12 +96,12 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.lp-btn:hover { border-color: rgba(251, 191, 36, 0.5); }
|
||||
.lp-btn:hover { border-color: var(--accent-line); }
|
||||
.lp-btn.lp-primary {
|
||||
background: linear-gradient(120deg, var(--lp-amber), var(--lp-orange));
|
||||
border-color: transparent;
|
||||
color: #14100a;
|
||||
box-shadow: 0 10px 30px rgba(245, 158, 11, 0.25);
|
||||
box-shadow: 0 10px 30px var(--accent-soft);
|
||||
}
|
||||
.lp-btn.lp-primary:hover { filter: brightness(1.07); }
|
||||
.lp-btn.lp-sm { padding: 9px 15px; font-size: 13px; }
|
||||
@@ -118,8 +121,21 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
.lp-logo em { font-style: normal; color: var(--lp-ink-amber); }
|
||||
.lp-logo-word { white-space: nowrap; }
|
||||
.lp-logo-mark { border-radius: 8px; display: block; width: 30px; height: 30px; }
|
||||
.lp-nav-links { display: flex; gap: 16px; margin-left: 14px; font-size: 12.5px; color: var(--lp-muted); }
|
||||
.lp-nav-links a:hover { color: var(--lp-text); }
|
||||
/* A short shelf of section jumps under the bar. Each one is a bordered pill,
|
||||
so it reads as a button; the text keeps the page's normal size while the
|
||||
shelf itself stays thin. */
|
||||
.lp-subnav { border-top: 1px solid var(--lp-line); }
|
||||
.lp-subnav .lp-wrap { display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px; height: auto; padding-top: 7px; padding-bottom: 7px; }
|
||||
.lp-subnav-link {
|
||||
padding: 4px 11px;
|
||||
border: 1px solid var(--lp-line);
|
||||
border-radius: 999px;
|
||||
color: var(--lp-muted);
|
||||
font-size: 14px;
|
||||
line-height: 1.35;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.lp-subnav-link:hover { color: var(--lp-text); border-color: var(--accent-line); }
|
||||
.lp-nav .lp-spacer { flex: 1 1 auto; }
|
||||
.lp-tools { display: flex; gap: 6px; }
|
||||
.lp-tool {
|
||||
@@ -135,13 +151,13 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.lp-tool:hover { color: var(--lp-text); border-color: rgba(251, 191, 36, 0.5); }
|
||||
.lp-tool:hover { color: var(--lp-text); border-color: var(--accent-line); }
|
||||
/* The theme popover borrows the workspace's .popover/.chip shell, re-skinned for
|
||||
the landing palette. */
|
||||
.lp-theme-pop { background: var(--lp-card); border-color: var(--lp-line); }
|
||||
.lp-theme-pop .chip { background: transparent; border-color: var(--lp-line); color: var(--lp-muted); }
|
||||
.lp-theme-pop .chip { background-color: transparent; border-color: var(--lp-line); color: var(--lp-muted); }
|
||||
.lp-theme-pop .chip:hover { color: var(--lp-text); }
|
||||
.lp-theme-pop .chip.on { color: var(--lp-ink-amber); border-color: var(--lp-ink-amber); background: rgba(251, 191, 36, 0.12); }
|
||||
.lp-theme-pop .chip.on { color: var(--lp-ink-amber); border-color: var(--lp-ink-amber); background-color: var(--accent-soft); }
|
||||
/* The account chip: a face + a name, opening the two things that account can
|
||||
do — its page (Admin / Profile) and log out. */
|
||||
.lp-account { display: inline-flex; align-items: center; gap: 7px; }
|
||||
@@ -157,11 +173,11 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
color: var(--lp-muted);
|
||||
text-decoration: none;
|
||||
}
|
||||
.lp-acc-pop .btn:hover { color: var(--lp-text); border-color: rgba(251, 191, 36, 0.5); }
|
||||
.lp-acc-pop .btn:hover { color: var(--lp-text); border-color: var(--accent-line); }
|
||||
.lp-burger { display: none; }
|
||||
|
||||
@media (max-width: 1160px) {
|
||||
.lp-nav-links, .lp-nav .lp-wrap > .lp-btn, .lp-nav .lp-account { display: none; }
|
||||
.lp-subnav, .lp-nav .lp-wrap > .lp-btn, .lp-nav .lp-account { display: none; }
|
||||
.lp-burger {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
@@ -186,13 +202,13 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
@media (min-width: 1161px) { .lp-sheet { display: none; } }
|
||||
|
||||
/* --- 2. hero ------------------------------------------------------------ */
|
||||
.lp-hero { position: relative; padding: 140px 0 74px; overflow: hidden; }
|
||||
.lp-hero { position: relative; padding: 184px 0 74px; overflow: hidden; }
|
||||
.lp-glow {
|
||||
position: absolute;
|
||||
inset: -30% -10% auto -10%;
|
||||
height: 560px;
|
||||
background:
|
||||
radial-gradient(420px 280px at 20% 30%, rgba(245, 158, 11, 0.22), transparent 70%),
|
||||
radial-gradient(420px 280px at 20% 30%, var(--accent-soft), transparent 70%),
|
||||
radial-gradient(380px 260px at 78% 22%, rgba(255, 94, 54, 0.16), transparent 70%),
|
||||
radial-gradient(420px 300px at 55% 80%, rgba(129, 140, 248, 0.14), transparent 70%);
|
||||
pointer-events: none;
|
||||
@@ -204,8 +220,8 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
gap: 8px;
|
||||
padding: 6px 13px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(251, 191, 36, 0.35);
|
||||
background: rgba(251, 191, 36, 0.08);
|
||||
border: 1px solid var(--accent-line);
|
||||
background: var(--accent-soft);
|
||||
color: var(--lp-ink-amber);
|
||||
font-family: var(--lp-mono);
|
||||
font-size: 11px;
|
||||
@@ -303,7 +319,7 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
font-family: var(--lp-mono);
|
||||
cursor: pointer;
|
||||
}
|
||||
.lp-tab[aria-pressed="true"] { border-color: var(--lp-amber); color: var(--lp-ink-amber); background: rgba(251, 191, 36, 0.1); }
|
||||
.lp-tab[aria-pressed="true"] { border-color: var(--lp-amber); color: var(--lp-ink-amber); background: var(--accent-soft); }
|
||||
.lp-view { position: relative; border-radius: 16px; overflow: hidden; border: 1px solid var(--lp-line); background: #000; }
|
||||
.lp-view img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; display: block; transition: filter 380ms ease; }
|
||||
.lp-hud {
|
||||
@@ -346,7 +362,7 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
/* --- 7. QR -------------------------------------------------------------- */
|
||||
.lp-qr { display: grid; grid-template-columns: 360px minmax(0, 1fr); gap: 34px; align-items: center; }
|
||||
.lp-recipe-card {
|
||||
border: 1px solid rgba(251, 191, 36, 0.3);
|
||||
border: 1px solid var(--accent-line);
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(160deg, #1b1d27, #0e0f14);
|
||||
padding: 16px;
|
||||
@@ -369,8 +385,8 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
border-radius: 999px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: rgba(251, 191, 36, 0.12);
|
||||
border: 1px solid rgba(251, 191, 36, 0.4);
|
||||
background: var(--accent-soft);
|
||||
border: 1px solid var(--accent-line);
|
||||
color: var(--lp-ink-amber);
|
||||
font-family: var(--lp-mono);
|
||||
font-size: 12px;
|
||||
@@ -381,7 +397,7 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
/* --- 8. pricing --------------------------------------------------------- */
|
||||
.lp-price { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; max-width: 860px; }
|
||||
.lp-plan { border: 1px solid var(--lp-line); background: var(--lp-card); border-radius: 18px; padding: 26px; display: flex; flex-direction: column; }
|
||||
.lp-plan.vip { border-color: rgba(251, 191, 36, 0.45); background: linear-gradient(180deg, rgba(245, 158, 11, 0.12), rgba(22, 24, 32, 0.9)); position: relative; }
|
||||
.lp-plan.vip { border-color: var(--accent-line); background: linear-gradient(180deg, var(--accent-soft), rgba(22, 24, 32, 0.9)); position: relative; }
|
||||
.lp-plan.vip::after {
|
||||
content: 'MOST POPULAR';
|
||||
position: absolute;
|
||||
@@ -439,7 +455,7 @@ html { scroll-behavior: smooth; scroll-padding-top: 78px; }
|
||||
bottom: 18px;
|
||||
z-index: 60;
|
||||
max-width: min(340px, calc(100vw - 36px));
|
||||
border: 1px solid rgba(251, 191, 36, 0.4);
|
||||
border: 1px solid var(--accent-line);
|
||||
background: rgba(22, 24, 32, 0.96);
|
||||
border-radius: 12px;
|
||||
padding: 12px 16px;
|
||||
|
||||
@@ -21,6 +21,12 @@ export function ThemeProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
useEffect(() => {
|
||||
applyTheme(mode, accent, fonts);
|
||||
// Auto has to follow the OS while the page is open, not just at mount.
|
||||
if (mode !== 'auto') return;
|
||||
const media = window.matchMedia('(prefers-color-scheme: light)');
|
||||
const onChange = () => applyTheme(mode, accent, fonts);
|
||||
media.addEventListener('change', onChange);
|
||||
return () => media.removeEventListener('change', onChange);
|
||||
}, [mode, accent, fonts]);
|
||||
|
||||
const setMode = useCallback((next: ThemeMode) => setModeState(next), []);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Theme data only — the CSS in styles/tokens.css owns the values. Adding an
|
||||
// accent group is one entry here plus one 1-line block in the stylesheet.
|
||||
|
||||
export type ThemeMode = 'light' | 'dark';
|
||||
export type ThemeMode = 'light' | 'dark' | 'auto';
|
||||
export type AccentId = 'amber' | 'blue' | 'emerald' | 'rose' | 'violet';
|
||||
export type FontSetId = 'studio' | 'editorial' | 'native';
|
||||
|
||||
@@ -23,7 +23,8 @@ export const FONT_SETS: { id: FontSetId; label: string; note: string }[] = [
|
||||
{ id: 'native', label: 'Thuần Việt', note: 'Be Vietnam Pro · Space Mono' },
|
||||
];
|
||||
|
||||
export const THEME_COLOR: Record<ThemeMode, string> = {
|
||||
// Keyed by the resolved mode only: auto never paints a browser chrome colour.
|
||||
export const THEME_COLOR: Record<'light' | 'dark', string> = {
|
||||
light: '#ffffff',
|
||||
dark: '#0a0a0a',
|
||||
};
|
||||
@@ -32,9 +33,17 @@ const KEY_THEME = 'rc.theme';
|
||||
const KEY_ACCENT = 'rc.accent';
|
||||
const KEY_FONTS = 'rc.fonts';
|
||||
|
||||
const MODES: ThemeMode[] = ['light', 'dark', 'auto'];
|
||||
|
||||
export function readTheme(): ThemeMode {
|
||||
const stored = localStorage.getItem(KEY_THEME);
|
||||
if (stored === 'light' || stored === 'dark') return stored;
|
||||
return MODES.includes(stored as ThemeMode) ? (stored as ThemeMode) : 'auto';
|
||||
}
|
||||
|
||||
// Auto follows the OS setting; everything downstream — the CSS, the persisted
|
||||
// value — only ever sees light or dark.
|
||||
export function resolveMode(mode: ThemeMode): 'light' | 'dark' {
|
||||
if (mode !== 'auto') return mode;
|
||||
return window.matchMedia?.('(prefers-color-scheme: light)').matches ? 'light' : 'dark';
|
||||
}
|
||||
|
||||
@@ -50,10 +59,10 @@ export function readFonts(): FontSetId {
|
||||
|
||||
export function applyTheme(mode: ThemeMode, accent: AccentId, fonts: FontSetId): void {
|
||||
const root = document.documentElement;
|
||||
root.dataset.theme = mode;
|
||||
root.dataset.theme = resolveMode(mode);
|
||||
root.dataset.accent = accent;
|
||||
root.dataset.fonts = fonts;
|
||||
document.querySelector('meta[name="theme-color"]')?.setAttribute('content', THEME_COLOR[mode]);
|
||||
document.querySelector('meta[name="theme-color"]')?.setAttribute('content', THEME_COLOR[resolveMode(mode)]);
|
||||
localStorage.setItem(KEY_THEME, mode);
|
||||
localStorage.setItem(KEY_ACCENT, accent);
|
||||
localStorage.setItem(KEY_FONTS, fonts);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useI18n, LANGS } from '../i18n/I18nProvider';
|
||||
import { ACCENTS, FONT_SETS, useTheme } from '../theme/ThemeProvider';
|
||||
import { ACCENTS, FONT_SETS, useTheme, type FontSetId } from '../theme/ThemeProvider';
|
||||
import { Avatar } from './Avatar';
|
||||
import type { User } from '../api';
|
||||
|
||||
@@ -88,6 +88,7 @@ export function TopBar({
|
||||
<div className="chip-row">
|
||||
<button type="button" className={`chip${mode === 'light' ? ' on' : ''}`} onClick={() => setMode('light')}>Light</button>
|
||||
<button type="button" className={`chip${mode === 'dark' ? ' on' : ''}`} onClick={() => setMode('dark')}>Dark</button>
|
||||
<button type="button" className={`chip${mode === 'auto' ? ' on' : ''}`} data-key="theme-auto" onClick={() => setMode('auto')}>Auto</button>
|
||||
</div>
|
||||
<div className="chip-row" style={{ marginTop: 8 }}>
|
||||
{ACCENTS.map((entry) => (
|
||||
@@ -101,20 +102,19 @@ export function TopBar({
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="chip-row" style={{ marginTop: 8 }}>
|
||||
{/* A dropdown keeps the group compact — the labels are long and the
|
||||
current choice is the only one that needs to be visible. */}
|
||||
<select
|
||||
className="chip font-select"
|
||||
data-key="fonts"
|
||||
title={FONT_SETS.find((f) => f.id === fonts)?.note}
|
||||
value={fonts}
|
||||
onChange={(e) => setFonts(e.target.value as FontSetId)}
|
||||
>
|
||||
{FONT_SETS.map((entry) => (
|
||||
<button
|
||||
key={entry.id}
|
||||
type="button"
|
||||
data-key={`fonts-${entry.id}`}
|
||||
title={entry.note}
|
||||
className={`chip${fonts === entry.id ? ' on' : ''}`}
|
||||
onClick={() => setFonts(entry.id)}
|
||||
>
|
||||
{entry.label}
|
||||
</button>
|
||||
<option key={entry.id} value={entry.id} title={entry.note}>{entry.label}</option>
|
||||
))}
|
||||
</div>
|
||||
</select>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user