···4040 sourceCode: "Source code",
4141 recentlyUpdated: "Recently updated",
4242 recentlyCreated: "Recently created",
4343+ sortBy: "Sort by",
4444+ filterByLanguage: "Filter by language",
4345 exploreMore: "Explore more",
4446 step1: "Log in with your ATProto account",
4547 step2: "Create a wiki or get invited to collaborate",
+2
src/lib/i18n/fr.ts
···4141 sourceCode: "Code source",
4242 recentlyUpdated: "Mis à jour récemment",
4343 recentlyCreated: "Créés récemment",
4444+ sortBy: "Trier par",
4545+ filterByLanguage: "Filtrer par langue",
4446 exploreMore: "Explorer plus",
4547 step1: "Connectez-vous avec votre compte ATProto",
4648 step2: "Créez un wiki ou faites-vous inviter pour collaborer",
···1818 .join(" ");
1919}
20202121+function themeVarsImportant(theme: Theme): string {
2222+ return Object.entries(theme)
2323+ .map(([k, v]) => {
2424+ const value = Array.isArray(v) ? v.join(", ") : (v as string);
2525+ return `--${kebab(k)}: ${value} !important;`;
2626+ })
2727+ .join(" ");
2828+}
2929+3030+// Tailwind typography (`prose`) ships its own --tw-prose-* palette baked into
3131+// dist.css. The plugin re-declares those vars *inside* `.prose { ... }`, which
3232+// shadows anything set further up the cascade — so we must match `.prose` to
3333+// redirect them at our theme tokens.
3434+const PROSE_OVERRIDES = [
3535+ "--tw-prose-body: var(--text-secondary)",
3636+ "--tw-prose-headings: var(--text)",
3737+ "--tw-prose-lead: var(--text-secondary)",
3838+ "--tw-prose-links: var(--accent)",
3939+ "--tw-prose-bold: var(--text)",
4040+ "--tw-prose-counters: var(--text-secondary)",
4141+ "--tw-prose-bullets: var(--text-secondary)",
4242+ "--tw-prose-hr: var(--border)",
4343+ "--tw-prose-quotes: var(--text-secondary)",
4444+ "--tw-prose-quote-borders: var(--accent-soft-border)",
4545+ "--tw-prose-captions: var(--text-muted)",
4646+ "--tw-prose-kbd: var(--text)",
4747+ "--tw-prose-code: var(--accent)",
4848+ "--tw-prose-pre-code: var(--text)",
4949+ "--tw-prose-pre-bg: var(--placeholder)",
5050+ "--tw-prose-th-borders: var(--border)",
5151+ "--tw-prose-td-borders: var(--border-subtle)",
5252+].join("; ");
5353+5454+const PROSE_RULE = `.prose { ${PROSE_OVERRIDES}; }`;
5555+5656+const SELECTION_RULE = `::selection { background-color: var(--accent-soft); color: var(--text); }`;
5757+5858+const REDUCED_MOTION_RULE = `@media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; } }`;
5959+6060+// `body, body *` with !important overrides any inline CSS vars (wiki-content
6161+// wrappers set their own when a wiki enforces a theme), forcing the printed
6262+// page to use the light palette regardless of user/wiki settings. Prose vars
6363+// reference our theme tokens, so resetting those is enough — no need to
6464+// re-declare prose vars here.
6565+const PRINT_RULE = `@media print { body, body * { ${themeVarsImportant(themes.light)}; color-scheme: light !important; } }`;
6666+6767+function bodyBlock(theme: Theme, colorScheme: string): string {
6868+ return `body { ${themeVars(theme)} color-scheme: ${colorScheme}; }`;
6969+}
7070+2171/**
2272 * Returns the contents of a <style> block that sets CSS theme vars on <body>.
2373 * For "system", emits both light defaults and a prefers-color-scheme: dark
2474 * override so the browser resolves the active theme natively (no FOUC, no JS).
7575+ *
7676+ * Also injects prose-typography overrides (so markdown headings/links/code
7777+ * follow the active theme), `::selection` styling, a reduced-motion guard,
7878+ * and a print stylesheet that always falls back to the light theme.
2579 */
2680export function themeRootStyle(userTheme: UserTheme): string {
2727- if (userTheme === "light") return `body { ${themeVars(themes.light)} }`;
2828- if (userTheme === "dark") return `body { ${themeVars(themes.dark)} }`;
2929- return `body { ${themeVars(themes.light)} } @media (prefers-color-scheme: dark) { body { ${themeVars(themes.dark)} } }`;
8181+ let block: string;
8282+ if (userTheme === "light") {
8383+ block = bodyBlock(themes.light, "light");
8484+ } else if (userTheme === "dark") {
8585+ block = bodyBlock(themes.dark, "dark");
8686+ } else {
8787+ block = `${bodyBlock(themes.light, "light dark")} @media (prefers-color-scheme: dark) { body { ${themeVars(themes.dark)} } }`;
8888+ }
8989+ return `${block} ${PROSE_RULE} ${SELECTION_RULE} ${REDUCED_MOTION_RULE} ${PRINT_RULE}`;
3090}
31913292function themeStyleAttr(theme: Theme): string {
···41101/**
42102 * Wraps wiki-content HTML in a div that overrides the chrome theme when the
43103 * wiki enforces its own. In reader mode the content inherits via CSS cascade
4444- * and no wrapper is emitted.
104104+ * and no wrapper is emitted. Prose vars set on <body> reference our base
105105+ * tokens, so they automatically follow the wiki's overrides in this scope.
45106 */
46107export function wrapWikiContent(
47108 html: string,