From 7a17e4e65dde6b3aa600f80a12760767f27b03bf Mon Sep 17 00:00:00 2001 From: Marco Farina Date: Sat, 27 Jun 2026 15:00:40 +0200 Subject: [PATCH 1/8] feat(home): icone duotone FA per le 4 card dei volumi MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sostituisce gli SVG line-style inline delle VolumeCard con le icone Font Awesome Pro 7.1.0 in stile duotone (primario currentColor + secondario opacity .4), una per accent del volume: - Vol 1 Programmatore → brackets-curly - Vol 2 Artefice → compass-drafting - Vol 3 Archivista → database - Vol 4 Apprendista → book-open-cover Il colore continua a derivare da --vol-icon-color, quindi nessuna modifica al CSS delle card. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01YbpGP9DBqBWuJKczky3Pxi --- src/pages/index.tsx | 113 ++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/src/pages/index.tsx b/src/pages/index.tsx index d1eed3e..e08af1d 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -10,81 +10,72 @@ import ChapterIndex from '@site/src/components/ChapterIndex'; import styles from './index.module.css'; -function TerminalIcon() { +/* + * Icone dei volumi: Font Awesome Pro 7.1.0 — stile duotone. + * Path primario `currentColor` + path secondario `opacity .4`, com'è nel + * sorgente FA. Il colore (e quindi i due livelli) deriva da `--vol-icon-color` + * impostato per accent dalla VolumeCard. viewBox originale di ciascuna icona. + */ +function BracketsCurlyIcon() { return ( -
-
{icon}
-
{title}
-
{body}
- Prossimamente -
- ); -} +const SQL_DEMO = `-- Modifica la query e ripremi Run +SELECT a.nome, COUNT(*) AS prestiti +FROM prestiti p +JOIN libri l ON l.id = p.libro_id +JOIN autori a ON a.id = l.autore_id +GROUP BY a.nome +ORDER BY prestiti DESC +LIMIT 3; +`; const FEATURES: Feature[] = [ { @@ -112,52 +176,83 @@ const FEATURES: Feature[] = [ }, { kicker: '02', - title: 'Algoritmi animati', - desc: 'Ordinamento, ricorsione, grafi: visualizzazioni interattive per vedere ogni passo.', - icon: , + title: 'Database dal vivo', + desc: 'Interroghi un vero SQLite nel browser. Zero installazioni: scrivi SQL e vedi le righe.', + icon: , demo: ( - } - title="Visualizzatore di algoritmi" - body={ - <> - Ogni algoritmo del libro avrà la sua animazione: step-by-step, - controlli play/pausa, evidenziazione delle variabili in gioco. - - } + ), }, { kicker: '03', + title: 'Algoritmi animati', + desc: 'Ordinamento, ricerca, ricorsione: visualizzazioni interattive per vedere ogni passo.', + icon: , + demo: , + }, + { + kicker: '04', title: 'Cerca, non sfogliare', desc: 'Trova subito un concetto, una definizione, un esempio. Senza indice analitico né rilegatura.', icon: , demo: ( - } - title="Ricerca full-text" - body={ - <> -
- - - ⌘K -
- Risultati istantanei con anteprima del contesto, scorciatoia da - tastiera, niente indice analitico da impaginare. - - } - /> +
+ +

+ Risultati istantanei con anteprima del contesto e scorciatoia da + tastiera. Niente indice analitico da impaginare. +

+
), }, ]; +interface MoreItem { + icon: ReactNode; + title: string; + desc: string; + color: string; +} + +const MORE: MoreItem[] = [ + { + icon: , + title: 'Percorsi adattivi', + desc: 'Il libro si rimodella sul tuo indirizzo: Informatica o Liceo Scienze Applicate.', + color: '#2563eb', + }, + { + icon: , + title: 'Spiegamelo facile', + desc: 'Un click e un assistente AI ti rispiega codice ed errori a parole tue.', + color: '#7c3aed', + }, + { + icon: , + title: 'Esercizi e teoria, collegati', + desc: 'Ogni esercizio sa da quale capitolo nasce; ogni capitolo sa come allenarti.', + color: '#0d9488', + }, +]; + interface CardProps { feature: Feature; index: number; @@ -223,7 +318,7 @@ export default function BentoFeatures() { Pensato per imparare facendo.

- Tre cose che un libro di testo, sulla carta, non può fare. + Cose che un libro di testo, sulla carta, non può fare.

+ +
+
E molto altro ancora…
+
    + {MORE.map((m) => ( +
  • + {m.icon} +
    +
    {m.title}
    +

    {m.desc}

    +
    +
  • + ))} +
+
); } diff --git a/src/components/BentoFeatures/styles.module.css b/src/components/BentoFeatures/styles.module.css index 105bfc1..5391828 100644 --- a/src/components/BentoFeatures/styles.module.css +++ b/src/components/BentoFeatures/styles.module.css @@ -30,11 +30,17 @@ .grid { display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(4, 1fr); gap: 16px; } @media (max-width: 900px) { + .grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (max-width: 560px) { .grid { grid-template-columns: 1fr; } @@ -179,86 +185,128 @@ } } -.comingWrap { - position: relative; +/* Box demo «Cerca»: un campo reale che apre la modale Pagefind (⌘K). */ +.searchPromo { border-radius: var(--radius-lg); - border: 1px dashed var(--at-border); + border: 1px solid var(--at-border); background: var(--at-bg-subtle); - padding: 36px 28px 28px; + padding: 40px 28px; text-align: center; - color: var(--at-muted); - font-size: 14px; - line-height: 1.6; } -.comingIcon { - width: 48px; - height: 48px; +.searchField { + display: flex; + align-items: center; + gap: 10px; + width: 100%; + max-width: 460px; + margin: 0 auto 18px; + padding: 12px 14px; border-radius: var(--radius-sm); - background: var(--at-bg-chip); border: 1px solid var(--at-border); - display: inline-flex; - align-items: center; - justify-content: center; - margin-bottom: 14px; + background: var(--at-bg); + color: var(--at-fg); + font: inherit; + text-align: left; + cursor: text; + transition: + border-color 200ms var(--ease-smooth), + box-shadow 200ms var(--ease-smooth); } -.comingTitle { - font-family: var(--font-display); - font-size: 20px; - font-weight: 600; - color: var(--at-fg-strong); - margin-bottom: 10px; - letter-spacing: -0.01em; +.searchField:hover, +.searchField:focus-visible { + border-color: var(--at-accent); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--at-accent) 18%, transparent); + outline: none; } -.comingBody { +.searchField svg { + width: 18px; + height: 18px; + flex-shrink: 0; +} + +.searchFieldText { + flex: 1; + color: var(--at-muted); +} + +.searchPromoNote { max-width: 460px; margin: 0 auto; + color: var(--at-muted); + font-size: 14px; + line-height: 1.6; } -.comingBadge { - position: absolute; - top: 14px; - right: 14px; +/* Blocco «E molto altro ancora»: peculiarità descrittive, non interattive. */ +.more { + margin-top: 56px; + padding-top: 36px; + border-top: 1px solid var(--at-border); +} + +.moreHead { + text-align: center; font-family: var(--font-mono-ui); - font-size: 10px; - letter-spacing: 0.14em; + font-size: 12px; + letter-spacing: 0.16em; text-transform: uppercase; - padding: 4px 8px; - border-radius: var(--radius-pill); - background: var(--at-bg-chip); - border: 1px solid var(--at-border); color: var(--at-muted); + margin-bottom: 28px; } -.fakeSearch { +.moreGrid { + list-style: none; + margin: 0; + padding: 0; + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 20px; +} + +@media (max-width: 760px) { + .moreGrid { + grid-template-columns: 1fr; + gap: 16px; + } +} + +.moreItem { display: flex; - align-items: center; - gap: 10px; - padding: 10px 14px; - border-radius: var(--radius-sm); - border: 1px solid var(--at-border); - background: var(--at-bg); - max-width: 420px; - margin: 8px auto 18px; + align-items: flex-start; + gap: 14px; text-align: left; } -.fakeSearch svg { - width: 18px; - height: 18px; +.moreIcon { flex-shrink: 0; + width: 40px; + height: 40px; + border-radius: var(--radius-sm); + display: inline-flex; + align-items: center; + justify-content: center; + color: var(--more-color); + background: color-mix(in srgb, var(--more-color) 12%, transparent); + border: 1px solid color-mix(in srgb, var(--more-color) 28%, transparent); } -.fakeSearch input { - flex: 1; - border: 0; - background: transparent; - color: var(--at-fg); - font: inherit; - outline: none; - cursor: not-allowed; +.moreItemTitle { + font-family: var(--font-display); + font-size: 17px; + font-weight: 600; + color: var(--at-fg-strong); + letter-spacing: -0.01em; + margin-bottom: 4px; +} + +.moreItemDesc { + margin: 0; + color: var(--at-muted); + font-size: 14px; + line-height: 1.55; } .fakeKbd { diff --git a/src/theme/SearchBar/index.tsx b/src/theme/SearchBar/index.tsx index e8dba65..3fa03bc 100644 --- a/src/theme/SearchBar/index.tsx +++ b/src/theme/SearchBar/index.tsx @@ -385,6 +385,19 @@ export default function SearchBar(): ReactNode { return () => document.removeEventListener('keydown', onKey); }, []); + // Apertura programmatica da altrove nella pagina (es. il box «Cerca» della + // homepage). L'evento è dispatchato sincronamente nel gesto utente, così + // warm() e il primer della tastiera (iOS) restano dentro l'interazione. + useEffect(() => { + const onOpen = () => { + warm(); + openKeyboardPrimer(); + setOpen(true); + }; + window.addEventListener('pdb:open-search', onOpen); + return () => window.removeEventListener('pdb:open-search', onOpen); + }, [warm]); + return ( <> - ))} +