Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ chrome competing with additions, deletions, syntax, or selected lines.
## Sources of truth

- `apps/web/src/tokens.css`: semantic colors, spacing, geometry, elevation,
responsive gutters, light/dark equivalents, and Omarchy theme palettes.
- `apps/web/src/theme.ts`: the theme registry (id, label, group, mode and
responsive gutters, light/dark equivalents, and theme palettes.
- `apps/web/src/theme.ts`: the theme registry (id, label, kind, mode and
matching code theme).
- `apps/web/src/typography.css`: fonts, text/icon sizes, weights, line heights.
- `apps/web/src/style.css`: Tailwind entry point, shadcn theme bridge, shell,
Expand Down Expand Up @@ -99,7 +99,7 @@ token that already has a utility is rejected. 2px (`*-0.5`) steps are optical
insets reserved for `components/`.

`[data-theme]` always carries the resolved light/dark mode; the Tailwind
`dark` variant and Pierre's `themeType` read it. Omarchy themes additionally set
`dark` variant and Pierre's `themeType` read it. Palette themes additionally set
`[data-palette]`, whose `tokens.css` block remaps every semantic color role, so
components never branch on a theme. Do not add independent theme state. Tailwind Preflight and utility use must not override Pierre's
measured elements or replace the application tokens with framework defaults.
Expand Down Expand Up @@ -145,15 +145,15 @@ measured elements or replace the application tokens with framework defaults.
the browser or whole machine. Keep that scope explicit in the status popup.
- Pierre owns syntax colors, changed-line fills, word highlights, line selection,
and code-theme surfaces. App status tokens govern the surrounding UI only.
Exception: Omarchy palettes without a bundled Shiki theme render code through
Exception: palettes without a bundled Shiki theme render code through
Pierre's CSS-variable theme (`diffx-palette`); their `tokens.css` blocks set
`--diffs-token-*`/`--diffs-ansi-*` and point Pierre's addition, deletion and
modified color overrides at `--success`, `--error` and `--warning`.

### Themes

- Settings offers three Diffx themes (System, Light, Dark) and the 22 themes on
omarchy.org. Each Omarchy theme has a fixed mode. The choice is saved per
- Settings offers three base themes (System, Light, Dark) and 22 palette
themes. Each palette theme has a fixed mode. The choice is saved per
browser (`theme-v2`); `index.html` applies it before the app renders and keeps
its palette id lists synchronized with `theme.ts`.
- Palettes keep each theme's background, foreground and accent identity, then
Expand Down Expand Up @@ -344,8 +344,8 @@ such detail into a token. New layout spacing must use the scale.
Add future system details as further sections in the same popover.
Settings follows it at the trailing edge: an icon-only compact ghost gear
that opens a `--surface-raised` popover titled “Settings”, capped at 24rem
and scrolling internally. Its Theme section is a choice list grouped under
“Diffx” and “Omarchy”; rows show a theme swatch, the name and a check on the
and scrolling internally. Its Theme section is one flat choice list, base
themes first; rows show a theme swatch, the name and a check on the
current theme. Clicking or arrowing applies and saves immediately; there is
no hover preview. Add future browser preferences as further sections.
- The diff display toolbar uses `--panel`, matching the header and status bar.
Expand Down Expand Up @@ -444,7 +444,7 @@ with a semantic variant instead of overriding it.
| Search or filter input | `search-field` |
| Text input, select, textarea, checkbox | `ui/input`, `ui/select`, `ui/textarea`, `ui/checkbox` |
| Tree, nav or picker row | `list-row` (sans/mono font, depth, `option`) |
| Single choice among rows (themes) | `choice-list` (groups, check on the chosen row), `theme-swatch` |
| Single choice among rows (themes) | `choice-list` (check on the chosen row), `theme-swatch` |
| Panel title row | `panel-header`; tabs in `ui/tabs` |
| Collapsible sidebar section | `collapsible-section`, `disclosure` chevron |
| Count, status label, status dot | `count-badge`, `ui/badge` (status tones), `status-dot` |
Expand Down
2 changes: 1 addition & 1 deletion apps/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta name="color-scheme" content="light dark" />
<script>
(() => {
// Keep these Omarchy ids synchronized with THEMES in src/theme.ts.
// Keep these palette ids synchronized with THEMES in src/theme.ts.
const lightPalettes = [
"catppuccin-latte",
"flexoki-light",
Expand Down
39 changes: 12 additions & 27 deletions apps/web/src/SettingsButton.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { SettingsIcon } from "lucide-react";
import {
ChoiceList,
ChoiceListGroup,
ChoiceListItem,
} from "@/components/choice-list";
import { ChoiceList, ChoiceListItem } from "@/components/choice-list";
import { IconButton } from "@/components/icon-button";
import {
Popover,
Expand All @@ -17,11 +13,6 @@ import { ThemeSwatch } from "@/components/theme-swatch";
import { useAppearance } from "./appearance-context.tsx";
import { readThemePreference, swatchPalette, THEMES } from "./theme.ts";

const groups = [
{ group: "diffx", label: "Diffx" },
{ group: "omarchy", label: "Omarchy" },
] as const;

/** Browser-saved preferences, opened from the status bar's trailing edge. */
export function SettingsButton() {
const { themePreference, setThemePreference } = useAppearance();
Expand All @@ -46,23 +37,17 @@ export function SettingsButton() {
setThemePreference(readThemePreference(value))
}
>
{groups.map(({ group, label }) => (
<ChoiceListGroup key={group} label={label}>
{THEMES.filter((theme) => theme.group === group).map(
(theme) => (
<ChoiceListItem key={theme.id} value={theme.id}>
<ThemeSwatch
palettes={
theme.id === "system"
? ["diffx-light", "diffx-dark"]
: [swatchPalette(theme.id)]
}
/>
{theme.label}
</ChoiceListItem>
),
)}
</ChoiceListGroup>
{THEMES.map((theme) => (
<ChoiceListItem key={theme.id} value={theme.id}>
<ThemeSwatch
palettes={
theme.id === "system"
? ["diffx-light", "diffx-dark"]
: [swatchPalette(theme.id)]
}
/>
{theme.label}
</ChoiceListItem>
))}
</ChoiceList>
</PopoverSection>
Expand Down
28 changes: 2 additions & 26 deletions apps/web/src/components/choice-list.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Radio } from "@base-ui/react/radio";
import { RadioGroup } from "@base-ui/react/radio-group";
import { CheckIcon } from "lucide-react";
import { useId, type ReactNode } from "react";
import type { ReactNode } from "react";
import { ListRow } from "@/components/list-row";
import { cn } from "@/lib/utils";

Expand All @@ -19,30 +19,6 @@ function ChoiceList({ className, ...props }: RadioGroup.Props<string>) {
);
}

/** Rows under a quiet heading that names the group for assistive tech. */
function ChoiceListGroup({
label,
children,
}: {
label: string;
children: ReactNode;
}) {
const labelId = useId();
return (
<div
data-slot="choice-list-group"
role="group"
aria-labelledby={labelId}
className="flex flex-col gap-0.5"
>
<div id={labelId} className="px-1 pt-2 pb-1 text-xs text-muted">
{label}
</div>
{children}
</div>
);
}

function ChoiceListItem({
value,
children,
Expand All @@ -60,4 +36,4 @@ function ChoiceListItem({
);
}

export { ChoiceList, ChoiceListGroup, ChoiceListItem };
export { ChoiceList, ChoiceListItem };
42 changes: 17 additions & 25 deletions apps/web/src/gallery/Gallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,7 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip";
import {
ChoiceList,
ChoiceListGroup,
ChoiceListItem,
} from "@/components/choice-list";
import { ChoiceList, ChoiceListItem } from "@/components/choice-list";
import { CollapsibleSection } from "@/components/collapsible-section";
import { ConfirmDialog } from "@/components/confirm-dialog";
import { ContextIcon } from "@/components/context-icon";
Expand Down Expand Up @@ -490,26 +486,22 @@ function Components() {
defaultValue="dark"
className="w-full max-w-(--drawer-width)"
>
<ChoiceListGroup label="Diffx">
<ChoiceListItem value="system">
<ThemeSwatch palettes={["diffx-light", "diffx-dark"]} />
System
</ChoiceListItem>
<ChoiceListItem value="dark">
<ThemeSwatch palettes={["diffx-dark"]} />
Dark
</ChoiceListItem>
</ChoiceListGroup>
<ChoiceListGroup label="Omarchy">
<ChoiceListItem value="tokyo-night">
<ThemeSwatch palettes={["tokyo-night"]} />
Tokyo Night
</ChoiceListItem>
<ChoiceListItem value="flexoki-light">
<ThemeSwatch palettes={["flexoki-light"]} />
Flexoki Light
</ChoiceListItem>
</ChoiceListGroup>
<ChoiceListItem value="system">
<ThemeSwatch palettes={["diffx-light", "diffx-dark"]} />
System
</ChoiceListItem>
<ChoiceListItem value="dark">
<ThemeSwatch palettes={["diffx-dark"]} />
Dark
</ChoiceListItem>
<ChoiceListItem value="tokyo-night">
<ThemeSwatch palettes={["tokyo-night"]} />
Tokyo Night
</ChoiceListItem>
<ChoiceListItem value="flexoki-light">
<ThemeSwatch palettes={["flexoki-light"]} />
Flexoki Light
</ChoiceListItem>
</ChoiceList>
</Section>

Expand Down
Loading
Loading