Follow-up to #27495, reopened as a high contrast mode change. Builds on the `high-contrast` class landed in #27555.
Muted UI text is written as text-gray-400 dark:text-gray-600. The grey scale in src/tailwind.css is achromatic oklch(L 0 0), so relative luminance is exactly L³: text-gray-400 is 2.07:1 on white and dark:text-gray-600 is 3.12:1 on #171717. The pair is effectively inverted, and both halves fail the 4.5:1 required by WCAG 1.4.3, with the light value also failing the 3:1 required of icons under 1.4.11. This is the text used for settings and admin section headings, field descriptions, sidebar labels, timestamps and counters, all at 10px to 12px, so the large-text exemption does not apply.
Rather than rewriting the class literal at 251 sites, the remap is two CSS rules that only apply when the existing High Contrast Mode setting is on, so the default theme is untouched:
- text-gray-400 resolves to gray-600 (5.75:1) in light mode
- dark:text-gray-600 resolves to gray-400 (8.65:1) in dark mode
dark:text-gray-500 already passes at 6.46:1 and is left alone.
The rules live in `@layer utilities` and use `:where()` to stay at low specificity: they outrank the base utility but lose to `hover:` and `dark:hover:` variants, so hover feedback keeps working. Verified in a browser against Tailwind's emitted rules and layer order: with the setting on, resting text resolves to gray-600 in light and gray-400 in dark, hover still resolves to its own value, and with the setting off nothing changes in either theme.
One component change is required alongside it. In admin/Settings/Audio.svelte the help text puts links inside the muted block via `[&_a]:text-gray-600`; once the surrounding prose resolves to gray-600 the link becomes the same colour as the text it sits in, and it has no resting underline, which would be a new WCAG 1.4.1 failure. The link moves to gray-900. This is the only place in the codebase where a link colour is nested inside muted text.
Letting the variants win has one edge: a few elements hover to a grey lighter than their new resting colour, so hovering would have lowered contrast instead of raising it. A light-mode hover landing on gray-500 now resolves to gray-800, which keeps the hover darker than the gray-600 resting state. Sidebar/Section.svelte is the site this branch would otherwise break.
Not covered here: text-gray-500 dark:text-gray-400 (2.77:1 in light), which is a separate branch.
Follow-up to #27497, reopened as a high contrast mode change, and the third and last of the contrast set after #27554 and #27555.
The grey scale in src/tailwind.css is achromatic oklch(L 0 0), so relative luminance is exactly L³ and text-gray-500 is 2.77:1 on white, against the 4.5:1 required by WCAG 1.4.3 and the 3:1 required of icons by 1.4.11. Around 290 sites use text-gray-500 dark:text-gray-400 for secondary labels, descriptions, counters and icons. Dark mode already passes at 6.46:1 and is left alone.
Rather than rewriting the class literal at every site, the remap is two CSS rules that only apply when the existing High Contrast Mode setting is on, so the default theme is untouched. Light mode resolves to gray-600 (5.75:1).
The split is not cosmetic. The `text-gray-500` utility is overridden inside `@layer utilities` with `:where()` so the rule sits below `hover:text-gray-*` and `dark:hover:text-gray-*` in specificity and hover feedback keeps working. The `.app-muted`, `.app-icon-muted` and `.tiptap table` classes in src/app.css are declared unlayered, which means no layered rule can reach them, so their override is unlayered too. They are `@apply text-gray-500 dark:text-gray-400` and fail identically, so leaving them out would have left the slash command menu and tiptap tables below 4.5:1 with the setting on.
Verified in a browser against Tailwind's emitted rules and layer order: with the setting on, the utility, .app-muted and .tiptap table all resolve to gray-600 in light while a focusable element carrying hover:text-gray-700 still resolves to gray-700 on interaction; dark mode and the setting-off case are unchanged in both themes.
One site is deliberately left out: EmbeddedChatHistoryDropdown.svelte uses text-gray-500/70, a separate class token that the selector does not match.
Follow-up to #27496, reopened as a high contrast mode change.
Placeholder text is the lowest contrast text in the product. The grey scale in src/tailwind.css is achromatic oklch(L 0 0), so relative luminance is exactly L³, and placeholder:text-gray-300 is 1.58:1 on white against the 4.5:1 required by WCAG 1.4.3. The large text exemption does not apply, the largest of these is text-lg. Placeholders are frequently the only format hint a field gives, for example admin/Settings/General.svelte uses e.g.) "http://localhost:3000".
Rather than deleting the ~200 per-component placeholder utilities and rewriting the base rule, the remap now happens in two CSS rules that only apply when the existing High Contrast Mode setting is on, so the default theme is untouched:
- placeholders resolve to gray-600 (5.75:1) in light mode
- placeholders resolve to gray-500 (6.46:1) in dark mode
The rules sit in `@layer utilities` and are anchored on `input`/`textarea`, which puts them above both the base rule in src/tailwind.css and every per-component `placeholder:text-*` utility, so no call site has to change. Placeholders stay distinguishable from real input values, which are text-gray-700 (8.46:1) in light and dark:text-gray-300 (11.39:1) in dark.
The chat composer placeholder is a tiptap ::before, so neither the base rule nor any utility reaches it. It is hardcoded #676767, which is 5.66:1 in light but only 3.17:1 on the dark canvas, so only the dark side is remapped, to gray-500. That rule stays outside the layer because the rule it overrides is unlayered too.
The root layout toggles a `high-contrast` class on documentElement from `$settings.highContrastMode`, alongside the existing theme classes, so every route is covered and the class is removed again when the setting is turned off.
Verified in a browser against Tailwind's emitted rules and layer order, on inputs both with and without per-component placeholder utilities: with the setting on, placeholders resolve to gray-600 in light and gray-500 in dark, the composer placeholder resolves to gray-500 in dark even with the prefers-color-scheme override treated as unconditional, and with the setting off nothing changes in either theme. Also checked against the oled-dark theme (gray-500 on #000 is 7.57:1) and the dark:bg-white/[0.03] input surface (6.01:1).
Note: the `high-contrast` class toggle is the same hunk as in the muted text contrast branch. Whichever lands first, the other rebases cleanly by dropping it.
Rework shimmer animation timing and gradient for a smoother sweep.
Slower start with acceleration through the text using a custom
cubic-bezier curve. Softer highlight contrast in both light and
dark modes.
* Add adjustable text size setting to interface
Introduces a user-configurable text size (scale) setting, accessible via a slider in the interface settings. Updates CSS and Sidebar chat item components to respect the new --app-text-scale variable, and persists the setting in the store. Adds related i18n strings and ensures the text scale is applied globally and clamped to allowed values.
* Refactor text scale logic into utility module
Moved all text scale related constants and functions from components and stores into a new utility module (src/lib/utils/text-scale.ts). Updated imports and usage in Interface.svelte and index.ts to use the new module, improving code organization and reusability.
* Adjust sidebar chat scaling without extra classes
keep sidebar markup using existing Tailwind utility classes so chat items render identically pre-feature
move all text-scale sizing into app.css under the #sidebar-chat-item selectors
change the root font-size multiplier to use 1rem instead of an explicit 16px so browser/user preferences propagate
* Update Switch.svelte
Adjust toggles from fixed pixel to rem to scale with the text size
* Update Interface.svelte
Updated label from 'Text Scale' to 'UI Scale'.
Added padding around slider
* Update app.css
Added comments
### UPD_Styles: Add dark mode styles for select elements and options.
Actually some select "boxes" have css dark theme support, but other not.
This PR add CSS for dark theme selects.
### UPD_Styles: Add dark mode styles for select elements and options.
Actually some select "boxes" have css dark theme support, but other not.
This PR add CSS for dark theme selects.