f2a5092253bd

Appearance: 4 display fonts (Cormorant Italic / Cormorant / Inter / System), 3 text sizes (small / default / large via zoom), reduce-motion toggle — all driven by data-attributes on html and CSS variables so desktop/mobile builds inherit the same plumbing (v2.6.0)

time
2026-04-27 17:36 UTC · 6 months ago
machine
koh2
parent
2d236b3fa7d9
9 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/background/vault.ts+21 −0src/popup/App.tsx+51 −0src/popup/styles/popup.css+93 −18src/popup/styles/vars.css+72 −0src/popup/views/Settings.tsx+68 −0src/popup/views/contracts.test.tsx+12 −0
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.5.1",4+  "version": "2.6.0",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach",4-  "version": "2.5.1",4+  "version": "2.6.0",55   "description": "Zero-knowledge password manager",66   "action": {77     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "2.5.1",3+  "version": "2.6.0",44   "description": "Zero-knowledge password manager",55   "type": "module",66   "scripts": {

view file

src/background/vault.ts+21 −0
@@ -58,6 +58,9 @@5858 // Settings (persisted inside the encrypted vault store as a sidecar key)5959 // ---------------------------------------------------------------------------6060 61+export type DisplayFont = "cormorant-italic" | "cormorant" | "inter" | "system";62+export type TextSize = "small" | "medium" | "large";63+6164 export interface VaultSettings {6265   accentColor?: AccentColorName;6366   theme?: "dark" | "light";@@ -69,6 +72,24 @@6972   trashRetentionDays?: number;7073   manualSummonDomains?: Record<string, ManualSummonRecord>;7174   declinedCardFingerprints?: string[];75+  /**76+   * Display font for headlines, brand wordmark, and narrative copy. Default77+   * is "cormorant-italic" — the bold italic serif that ships as Peach's78+   * voice. Non-italic options exist for users who prefer upright type.79+   */80+  displayFont?: DisplayFont;81+  /**82+   * Text size scale for body copy and entry titles. Stops at "large" by83+   * design — XL clips dense screens (Vault, Health) inside the fixed84+   * 330×480 popup surface.85+   */86+  textSize?: TextSize;87+  /**88+   * When true, all entrance/exit transitions and ambient animations are89+   * collapsed to instant. Honoured alongside the user's OS-level90+   * `prefers-reduced-motion`.91+   */92+  reduceMotion?: boolean;7293   /**7394    * User overrides for keyboard shortcuts. Keys are ShortcutAction names7495    * (see src/shared/shortcuts.ts). Missing actions fall through to

view file

src/popup/App.tsx+51 −0
@@ -174,6 +174,9 @@174174   const [editEntry, setEditEntry] = createSignal<VaultEntry | undefined>(undefined);175175   const [accentColor, setAccentColor] = createSignal<AccentColorName>("peach");176176   const [theme, setTheme] = createSignal<"dark" | "light">("dark");177+  const [displayFont, setDisplayFont] = createSignal<"cormorant-italic" | "cormorant" | "inter" | "system">("cormorant-italic");178+  const [textSize, setTextSize] = createSignal<"small" | "medium" | "large">("medium");179+  const [reduceMotion, setReduceMotion] = createSignal<boolean>(false);177180   const [lockOnClose, setLockOnClose] = createSignal(true);178181   const [autoLockMinutes, setAutoLockMinutes] = createSignal(5);179182   const [breachMonitoring, setBreachMonitoring] = createSignal(true);@@ -218,6 +221,22 @@218221     document.body.classList.toggle("light", theme() === "light");219222   });220223 224+  createEffect(() => {225+    document.documentElement.setAttribute("data-display-font", displayFont());226+  });227+228+  createEffect(() => {229+    document.documentElement.setAttribute("data-text-size", textSize());230+  });231+232+  createEffect(() => {233+    if (reduceMotion()) {234+      document.documentElement.setAttribute("data-motion", "reduce");235+    } else {236+      document.documentElement.removeAttribute("data-motion");237+    }238+  });239+221240   const selectedIndex = createMemo(() => {222241     const currentId = selectedEntry()?.id;223242     if (!currentId) return entries().length > 0 ? 0 : -1;@@ -235,6 +254,21 @@235254     await sendMessage({ type: "UPDATE_SETTINGS", payload: { theme: t } });236255   }237256 257+  async function applyDisplayFont(f: "cormorant-italic" | "cormorant" | "inter" | "system") {258+    setDisplayFont(f);259+    await sendMessage({ type: "UPDATE_SETTINGS", payload: { displayFont: f } });260+  }261+262+  async function applyTextSize(size: "small" | "medium" | "large") {263+    setTextSize(size);264+    await sendMessage({ type: "UPDATE_SETTINGS", payload: { textSize: size } });265+  }266+267+  async function applyReduceMotion(enabled: boolean) {268+    setReduceMotion(enabled);269+    await sendMessage({ type: "UPDATE_SETTINGS", payload: { reduceMotion: enabled } });270+  }271+238272   // Persist only the deltas vs DEFAULT_BINDINGS — keeps the vault payload239273   // small and lets future default updates land for actions the user hasn't240274   // touched. Settings sends us the full active map; we filter and store.@@ -537,6 +571,17 @@537571       setAccentColor(s.accentColor as AccentColorName);538572     }539573     setTheme(s.theme === "light" ? "light" : "dark");574+    if (s.displayFont === "cormorant" || s.displayFont === "inter" || s.displayFont === "system") {575+      setDisplayFont(s.displayFont);576+    } else {577+      setDisplayFont("cormorant-italic");578+    }579+    if (s.textSize === "small" || s.textSize === "large") {580+      setTextSize(s.textSize);581+    } else {582+      setTextSize("medium");583+    }584+    setReduceMotion(s.reduceMotion === true);540585     setLockOnClose(s.lockOnClose !== false);541586     setAutoLockMinutes(typeof s.autoLockMinutes === "number" ? s.autoLockMinutes : 5);542587     setBreachMonitoring(s.breachMonitoring !== false);@@ -972,6 +1017,9 @@9721017                   trashRetentionDays={trashRetentionDays()}9731018                   theme={theme()}9741019                   accentColor={accentColor()}1020+                  displayFont={displayFont()}1021+                  textSize={textSize()}1022+                  reduceMotion={reduceMotion()}9751023                   isPro={proStatus()}9761024                   initialSection={settingsInitialSection()}9771025                   upgradeHighlight={upgradeHighlight()}@@ -981,6 +1029,9 @@9811029                   onTrashRetentionChange={applyTrashRetention}9821030                   onThemeChange={applyTheme}9831031                   onAccentChange={applyAccentColor}1032+                  onDisplayFontChange={applyDisplayFont}1033+                  onTextSizeChange={applyTextSize}1034+                  onReduceMotionChange={applyReduceMotion}9841035                   shortcuts={shortcutBindings()}9851036                   onShortcutsChange={applyShortcuts}9861037                   onLicenseChange={refreshProStatus}

view file

src/popup/styles/popup.css+93 −18
@@ -164,7 +164,7 @@164164 /* Wordmark */165165 .peach-wordmark {166166   margin: 0;167-  font-family: "Cormorant Garamond", "Times New Roman", serif;167+  font-family: var(--font-display);168168   font-size: 15px;169169   font-style: italic;170170   letter-spacing: 0.03em;@@ -297,7 +297,7 @@297297 .peach-entry-name {298298   margin: 0;299299   padding-bottom: 0.12em;300-  font-family: "Cormorant Garamond", "Times New Roman", serif;300+  font-family: var(--font-display);301301   font-style: italic; font-weight: 400;302302   font-size: 44px; line-height: 0.95;303303   color: var(--peach-t0);@@ -466,7 +466,7 @@466466 .peach-note-content {467467   border-top: 1px solid var(--peach-t3);468468   padding-top: 10px;469-  font-family: "Cormorant Garamond", "Times New Roman", serif;469+  font-family: var(--font-display);470470   font-size: 15px;471471   line-height: 1.75;472472   color: var(--peach-t1);@@ -508,7 +508,7 @@508508   gap: 6px;509509 }510510 .peach-inline-title-input {511-  font-family: "Cormorant Garamond", "Times New Roman", serif;511+  font-family: var(--font-display);512512   font-style: italic;513513   font-weight: 400;514514   font-size: 40px;@@ -717,7 +717,7 @@717717 }718718 .peach-totp-code {719719   margin: 2px 0 0;720-  font-family: "Cormorant Garamond", "Times New Roman", serif;720+  font-family: var(--font-display);721721   font-style: italic;722722   font-weight: 300;723723   font-size: 38px; color: var(--peach-accent);@@ -792,7 +792,7 @@792792 .peach-search-icon { color: var(--peach-t2); display: inline-flex; align-items: center; }793793 .peach-search-input {794794   border: 0; background: transparent; color: var(--peach-t0);795-  font-family: "Cormorant Garamond", "Times New Roman", serif;795+  font-family: var(--font-display);796796   font-style: italic; font-size: 18px;797797   outline: none; width: 100%; min-width: 0; padding: 0;798798   caret-color: var(--peach-accent);@@ -915,7 +915,7 @@915915 /* Unlock screen elements */916916 .peach-unlock-headline {917917   margin: 2px 0 0;918-  font-family: "Cormorant Garamond", "Times New Roman", serif;918+  font-family: var(--font-display);919919   font-size: 52px; line-height: 0.95;920920   font-style: italic; font-weight: 400;921921 }@@ -1381,7 +1381,7 @@13811381 .peach-entry-row-info { flex: 1; min-width: 0; }13821382 .peach-entry-row-name {13831383   display: block;1384-  font-family: "Cormorant Garamond", serif;1384+  font-family: var(--font-display);13851385   font-size: 17px;13861386   font-style: italic;13871387   font-weight: 400;@@ -2324,7 +2324,7 @@23242324   outline: none;23252325   width: 100%;23262326   color: var(--peach-t0);2327-  font-family: "Cormorant Garamond", Georgia, serif;2327+  font-family: var(--font-display);23282328   font-style: italic;23292329   font-weight: 400;23302330   font-size: 40px;@@ -2410,7 +2410,7 @@24102410   outline: none;24112411   width: 100%;24122412   color: var(--peach-t0);2413-  font-family: "Cormorant Garamond", Georgia, serif;2413+  font-family: var(--font-display);24142414   font-style: italic;24152415   font-weight: 400;24162416   font-size: 40px;@@ -2672,7 +2672,7 @@26722672 26732673 .peach-note-editor-label {26742674   flex: 1;2675-  font-family: "Cormorant Garamond", "Times New Roman", serif;2675+  font-family: var(--font-display);26762676   font-style: italic;26772677   font-size: 15px;26782678   color: var(--peach-t1);@@ -2746,7 +2746,7 @@27462746   overflow-y: auto;27472747   padding: 16px 20px 20px;27482748   outline: none;2749-  font-family: "Cormorant Garamond", "Times New Roman", serif;2749+  font-family: var(--font-display);27502750   font-size: 16px;27512751   line-height: 1.75;27522752   color: var(--peach-t0);@@ -2835,7 +2835,7 @@28352835 /* Rich content element styles (editor + display) */28362836 .peach-note-editor-body h2,28372837 .peach-note-content h2 {2838-  font-family: "Cormorant Garamond", serif;2838+  font-family: var(--font-display);28392839   font-size: 20px;28402840   font-weight: 600;28412841   font-style: italic;@@ -2945,7 +2945,7 @@29452945   border: none;29462946   border-bottom: 1px solid var(--peach-t3);29472947   padding: 6px 0;2948-  font-family: "Cormorant Garamond", Georgia, serif;2948+  font-family: var(--font-display);29492949   font-style: italic;29502950   font-size: 28px;29512951   color: var(--peach-t0);@@ -3083,7 +3083,7 @@30833083   flex: 1;30843084   min-width: 0;30853085   margin: 0;3086-  font-family: "Cormorant Garamond", "Times New Roman", serif;3086+  font-family: var(--font-display);30873087   font-style: italic;30883088   font-weight: 300;30893089   font-size: 32px;@@ -3240,7 +3240,7 @@32403240 32413241 .peach-onboarding-headline {32423242   margin: 0;3243-  font-family: "Cormorant Garamond", "Times New Roman", serif;3243+  font-family: var(--font-display);32443244   font-size: 35px;32453245   line-height: 0.88;32463246   font-style: italic;@@ -4271,7 +4271,7 @@42714271 }42724272 42734273 .peach-health-row-name {4274-  font-family: "Cormorant Garamond", serif;4274+  font-family: var(--font-display);42754275   font-size: 15px;42764276   font-style: italic;42774277   font-weight: 400;@@ -5063,6 +5063,81 @@50635063   transform: scale(1.1);50645064 }50655065 5066+/* Display font picker — swatch shows an "Aa" preview rendered with each5067+   font option so the user can see what they're picking before they pick. */5068+.peach-settings-row--font { align-items: flex-start; padding-top: 10px; }5069+.peach-font-picker {5070+  display: flex;5071+  flex-wrap: wrap;5072+  gap: 6px;5073+  padding: 2px 0;5074+  flex: 1;5075+  justify-content: flex-end;5076+}5077+.peach-font-swatch {5078+  display: flex;5079+  flex-direction: column;5080+  align-items: center;5081+  gap: 2px;5082+  padding: 4px 8px;5083+  background: transparent;5084+  border: 1px solid var(--peach-t3);5085+  border-radius: 6px;5086+  cursor: pointer;5087+  transition: border-color 100ms, background 100ms;5088+  min-width: 56px;5089+}5090+.peach-font-swatch:hover {5091+  border-color: var(--peach-accent-dim);5092+  background: color-mix(in srgb, var(--peach-accent) 4%, transparent);5093+}5094+.peach-font-swatch.is-active {5095+  border-color: var(--peach-accent);5096+  background: color-mix(in srgb, var(--peach-accent) 8%, transparent);5097+}5098+.peach-font-swatch-glyph {5099+  font-size: 18px;5100+  line-height: 1;5101+  color: var(--peach-t0);5102+}5103+.peach-font-swatch[data-font-preview="cormorant-italic"] .peach-font-swatch-glyph {5104+  font-family: "Cormorant Garamond", serif;5105+  font-style: italic;5106+  font-weight: 400;5107+}5108+.peach-font-swatch[data-font-preview="cormorant"] .peach-font-swatch-glyph {5109+  font-family: "Cormorant Garamond", serif;5110+  font-style: normal;5111+  font-weight: 400;5112+}5113+.peach-font-swatch[data-font-preview="inter"] .peach-font-swatch-glyph {5114+  font-family: "Inter Variable", "Inter", sans-serif;5115+  font-style: normal;5116+  font-weight: 400;5117+}5118+.peach-font-swatch[data-font-preview="system"] .peach-font-swatch-glyph {5119+  font-family: system-ui, -apple-system, sans-serif;5120+  font-style: normal;5121+  font-weight: 400;5122+}5123+.peach-font-swatch-name {5124+  font-family: var(--font-mono);5125+  font-size: 8px;5126+  letter-spacing: 0.06em;5127+  color: var(--peach-t2);5128+  text-transform: lowercase;5129+}5130+5131+.peach-pill-group {5132+  display: flex;5133+  gap: 6px;5134+}5135+.peach-pill-group .peach-btn.is-active {5136+  background: color-mix(in srgb, var(--peach-accent) 14%, transparent);5137+  border-color: var(--peach-accent);5138+  color: var(--peach-t0);5139+}5140+50665141 /* ── Card entry styles ───────────────────────────────────────── */50675142 .peach-card-number {50685143   font-family: var(--peach-mono);@@ -5437,7 +5512,7 @@54375512 }54385513 54395514 .peach-pro-wordmark {5440-  font-family: "Cormorant Garamond", "Times New Roman", serif;5515+  font-family: var(--font-display);54415516   font-size: 22px;54425517   font-style: italic;54435518   font-weight: 400;

view file

src/popup/styles/vars.css+72 −0
@@ -103,8 +103,13 @@103103   --totp-tag: rgba(120, 175, 255, 0.75);104104   --copy-shadow: 0 2px 8px rgba(0,0,0,0.55), 0 14px 48px rgba(0,0,0,0.7), 0 0 0 1px rgba(240,232,216,0.04);105105   --font-display: "Cormorant Garamond", "Times New Roman", serif;106+  --font-display-style: italic;106107   --font-mono: "IBM Plex Mono", "SFMono-Regular", Menlo, monospace;107108   --font-ui: "Inter Variable", "Inter", system-ui, sans-serif;109+  /* Body-text scale; multiplied into key font-size declarations so users110+     can request small / default / large without breaking dense layouts.111+     XL was deliberately excluded — clips Vault and Health. */112+  --peach-text-scale: 1;108113   --spring: cubic-bezier(0.16, 1, 0.3, 1);109114   --retreat: cubic-bezier(0.4, 0, 1, 1);110115   --settle: cubic-bezier(0.34, 1.56, 0.64, 1);@@ -163,4 +168,71 @@163168   --shell-accent-a: color-mix(in srgb, var(--peach-accent) 6%, transparent);164169   --shell-accent-b: color-mix(in srgb, var(--peach-accent) 3%, transparent);165170   --copy-shadow: 0 2px 8px rgba(80, 56, 32, 0.10), 0 14px 48px rgba(80, 56, 32, 0.10), 0 0 0 1px rgba(28,25,21,0.05);171+}172+173+/* ─── Display font choice ──────────────────────────────────────────────174+   Default is Cormorant Italic. The non-italic options swap both the175+   family AND the upright variant so display copy doesn't render as176+   a synthesised slant on fonts that lack a true italic weight. */177+[data-display-font="cormorant-italic"] {178+  --font-display: "Cormorant Garamond", "Times New Roman", serif;179+  --font-display-style: italic;180+}181+[data-display-font="cormorant"] {182+  --font-display: "Cormorant Garamond", "Times New Roman", serif;183+  --font-display-style: normal;184+}185+[data-display-font="inter"] {186+  --font-display: "Inter Variable", "Inter", system-ui, sans-serif;187+  --font-display-style: normal;188+}189+[data-display-font="system"] {190+  --font-display: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;191+  --font-display-style: normal;192+}193+194+/* When the user picks an upright font, neutralise the italic-by-default195+   styling on the display surfaces so the result feels intentional. */196+:root[data-display-font="cormorant"] .peach-wordmark,197+:root[data-display-font="cormorant"] .peach-wordmark-wrap,198+:root[data-display-font="cormorant"] .peach-pro-headline,199+:root[data-display-font="cormorant"] .peach-pro-mini-badge,200+:root[data-display-font="inter"] .peach-wordmark,201+:root[data-display-font="inter"] .peach-wordmark-wrap,202+:root[data-display-font="inter"] .peach-pro-headline,203+:root[data-display-font="inter"] .peach-pro-mini-badge,204+:root[data-display-font="system"] .peach-wordmark,205+:root[data-display-font="system"] .peach-wordmark-wrap,206+:root[data-display-font="system"] .peach-pro-headline,207+:root[data-display-font="system"] .peach-pro-mini-badge {208+  font-style: normal;209+}210+211+/* ─── Text size scaling ────────────────────────────────────────────────212+   Implemented via the `zoom` property on the popup body so every existing213+   px-based font-size in popup.css scales proportionally without rewriting214+   any individual rule. Capped at 1.08 — large enough to noticeably help,215+   small enough that the dense Vault and Health views don't clip the216+   fixed-size popup surface. The CSS custom property is also exposed so217+   desktop / mobile builds can use a calc() multiplier on layouts that218+   don't fit `zoom` semantics. */219+[data-text-size="small"]  { --peach-text-scale: 0.92; }220+[data-text-size="medium"] { --peach-text-scale: 1; }221+[data-text-size="large"]  { --peach-text-scale: 1.08; }222+html[data-text-size="small"]  body { zoom: 0.92; }223+html[data-text-size="large"]  body { zoom: 1.08; }224+225+/* ─── Reduced motion ───────────────────────────────────────────────────226+   User-explicit setting OR OS-level prefers-reduced-motion both collapse227+   transitions and ambient animations to instant. The user-explicit form228+   takes precedence and works even when the OS hasn't been configured. */229+[data-motion="reduce"] *,230+[data-motion="reduce"] *::before,231+[data-motion="reduce"] *::after {232+  animation-duration: 0.001ms !important;233+  animation-delay: 0.001ms !important;234+  animation-iteration-count: 1 !important;235+  transition-duration: 0.001ms !important;236+  transition-delay: 0.001ms !important;237+  scroll-behavior: auto !important;166238 }

view file

src/popup/views/Settings.tsx+68 −0
@@ -28,6 +28,9 @@2828   trashRetentionDays: number;2929   theme: "dark" | "light";3030   accentColor: AccentColorName;31+  displayFont: "cormorant-italic" | "cormorant" | "inter" | "system";32+  textSize: "small" | "medium" | "large";33+  reduceMotion: boolean;3134   isPro?: boolean;3235   initialSection?: string | null;3336   upgradeHighlight?: ProFeature | null;@@ -37,6 +40,9 @@3740   onTrashRetentionChange: (days: number) => void | Promise<void>;3841   onThemeChange: (theme: "dark" | "light") => void;3942   onAccentChange: (name: AccentColorName) => void | Promise<void>;43+  onDisplayFontChange: (font: "cormorant-italic" | "cormorant" | "inter" | "system") => void | Promise<void>;44+  onTextSizeChange: (size: "small" | "medium" | "large") => void | Promise<void>;45+  onReduceMotionChange: (enabled: boolean) => void | Promise<void>;4046   shortcuts: Record<ShortcutAction, Keybinding>;4147   onShortcutsChange: (active: Record<ShortcutAction, Keybinding>) => void | Promise<void>;4248   onLicenseChange?: () => void | Promise<void>;@@ -1080,6 +1086,17 @@10801086   }10811087 10821088   function renderAppearance() {1089+    const fontOptions: Array<{ id: "cormorant-italic" | "cormorant" | "inter" | "system"; label: string; preview: string }> = [1090+      { id: "cormorant-italic", label: "Cormorant Italic", preview: "Aa" },1091+      { id: "cormorant", label: "Cormorant", preview: "Aa" },1092+      { id: "inter", label: "Inter", preview: "Aa" },1093+      { id: "system", label: "System", preview: "Aa" },1094+    ];1095+    const sizeOptions: Array<{ id: "small" | "medium" | "large"; label: string }> = [1096+      { id: "small", label: "Small" },1097+      { id: "medium", label: "Default" },1098+      { id: "large", label: "Large" },1099+    ];10831100     return (10841101       <div class="peach-settings-body">10851102         <div class="peach-settings-group">@@ -1113,6 +1130,57 @@11131130                 )}11141131               </For>11151132             </div>1133+          </div>1134+1135+          <p class="peach-settings-sub-label">DISPLAY FONT</p>1136+          <div class="peach-settings-row peach-settings-row--meta peach-settings-row--font">1137+            <span>Font</span>1138+            <div class="peach-font-picker">1139+              <For each={fontOptions}>1140+                {(opt) => (1141+                  <button1142+                    class="peach-font-swatch"1143+                    classList={{ "is-active": props.displayFont === opt.id }}1144+                    data-font-preview={opt.id}1145+                    onClick={() => void props.onDisplayFontChange(opt.id)}1146+                    aria-label={opt.label}1147+                    title={opt.label}1148+                  >1149+                    <span class="peach-font-swatch-glyph">{opt.preview}</span>1150+                    <span class="peach-font-swatch-name">{opt.label}</span>1151+                  </button>1152+                )}1153+              </For>1154+            </div>1155+          </div>1156+1157+          <p class="peach-settings-sub-label">TEXT SIZE</p>1158+          <div class="peach-settings-row peach-settings-row--meta">1159+            <span>Size</span>1160+            <div class="peach-pill-group">1161+              <For each={sizeOptions}>1162+                {(opt) => (1163+                  <button1164+                    class="peach-btn peach-btn--ghost peach-btn--sm"1165+                    classList={{ "is-active": props.textSize === opt.id }}1166+                    onClick={() => void props.onTextSizeChange(opt.id)}1167+                  >1168+                    {opt.label}1169+                  </button>1170+                )}1171+              </For>1172+            </div>1173+          </div>1174+1175+          <p class="peach-settings-sub-label">MOTION</p>1176+          <div class="peach-settings-row peach-settings-row--meta">1177+            <span>Reduce motion</span>1178+            <button1179+              class="peach-btn peach-btn--subtle peach-btn--sm"1180+              onClick={() => void props.onReduceMotionChange(!props.reduceMotion)}1181+            >1182+              {props.reduceMotion ? "On" : "Off"}1183+            </button>11161184           </div>11171185         </div>11181186       </div>

view file

src/popup/views/contracts.test.tsx+12 −0
@@ -246,12 +246,18 @@246246         trashRetentionDays={30}247247         theme="dark"248248         accentColor="peach"249+        displayFont="cormorant-italic"250+        textSize="medium"251+        reduceMotion={false}249252         onBreachMonitoringChange={vi.fn()}250253         onLockOnCloseChange={vi.fn()}251254         onAutoLockMinutesChange={vi.fn()}252255         onTrashRetentionChange={vi.fn()}253256         onThemeChange={vi.fn()}254257         onAccentChange={vi.fn()}258+        onDisplayFontChange={vi.fn()}259+        onTextSizeChange={vi.fn()}260+        onReduceMotionChange={vi.fn()}255261         shortcuts={DEFAULT_BINDINGS}256262         onShortcutsChange={vi.fn()}257263         onBack={vi.fn()}@@ -303,12 +309,18 @@303309         trashRetentionDays={30}304310         theme="dark"305311         accentColor="peach"312+        displayFont="cormorant-italic"313+        textSize="medium"314+        reduceMotion={false}306315         onBreachMonitoringChange={vi.fn()}307316         onLockOnCloseChange={vi.fn()}308317         onAutoLockMinutesChange={onAutoLockMinutesChange}309318         onTrashRetentionChange={vi.fn()}310319         onThemeChange={onThemeChange}311320         onAccentChange={vi.fn()}321+        onDisplayFontChange={vi.fn()}322+        onTextSizeChange={vi.fn()}323+        onReduceMotionChange={vi.fn()}312324         shortcuts={DEFAULT_BINDINGS}313325         onShortcutsChange={vi.fn()}314326         onBack={vi.fn()}

view file