Skip to content

feat: light/dark theme toggle#29

Merged
mickamy merged 3 commits into
mainfrom
feat/ui-light-mode-toggle
Jun 30, 2026
Merged

feat: light/dark theme toggle#29
mickamy merged 3 commits into
mainfrom
feat/ui-light-mode-toggle

Conversation

@mickamy

@mickamy mickamy commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the last open piece of the a11y polish roadmap item: a light/dark theme toggle. The UI was authored dark-only (dark zinc colors applied unconditionally), so this adds a coherent light palette as the new default-following-OS behavior while keeping the existing dark look byte-for-byte.

What landed

Surface Change
internal/ui/templates/layout.html A head script sets the theme class before the stylesheet applies (no flash): an explicit choice in localStorage wins, otherwise it follows prefers-color-scheme, defaulting to dark if unavailable. A sidebar toggle button (🌙 / ☀) flips the class and persists the choice. The hardcoded class="dark" on <html> is removed.
All 7 templates The ~192 unconditional color utilities are now light dark:dark pairs (e.g. bg-zinc-900bg-white dark:bg-zinc-900; the inverted primary button bg-zinc-100bg-zinc-900 dark:bg-zinc-100). In dark mode the dark: variants reproduce the previous colors exactly, so the dark theme is unchanged.
internal/ui/templates/content_table.html, layout.html The two classList.toggle('bg-zinc-800', on) selection highlights (row nav, palette active option) now toggle two tokens (bg-zinc-200 + dark:bg-zinc-800) since classList.toggle takes one class each.
internal/ui/static/css/tailwind.css Regenerated via make ui-css (light utilities + dark: variants). darkMode: 'class' was already set in tailwind.config.js.
internal/ui/ui_test.go Three assertions that hardcoded a swept color class are updated to the new light dark:dark form.
README.md Ticks the a11y polish roadmap item — all three sub-items (skeleton loaders, empty / error states, light-mode toggle) are now done.

Design

  • Dark colors are preserved as the dark: variants, so existing dark-mode users see no visual change; light is added as a new layer rather than a rewrite.
  • The default follows the OS (prefers-color-scheme); an explicit toggle is remembered in localStorage under adms-theme. Users on a dark OS keep the current experience by default.
  • The light palette inverts the zinc scale with hierarchy preserved (body zinc-50, surfaces white, borders zinc-200, primary/secondary/muted text zinc-900/700/600, primary button inverted to dark), and the danger (red) accents shift to light-readable shades.

Test plan

  • make test -race — all packages green (UI assertions updated for the swept classes).
  • make lint — 0 issues.
  • make ui-css — regenerated; ui-css-drift will not fail.
  • Verified both themes in a real browser (headless Chrome via Puppeteer): table, schema, new-row form, and the command palette render correctly in light; dark is identical to before; Cmd+K opens the palette.

@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.30%. Comparing base (7d58baf) to head (fde0f23).

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #29   +/-   ##
=======================================
  Coverage   87.30%   87.30%           
=======================================
  Files          37       37           
  Lines        2418     2418           
=======================================
  Hits         2111     2111           
  Misses        205      205           
  Partials      102      102           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a light-mode toggle and theme-aware styling across the admin UI templates, updating Tailwind CSS configurations and layout files to support both light and dark themes. The review feedback identifies several accessibility issues where the contrast ratio of muted helper text (using text-zinc-400 on light backgrounds) falls below the WCAG AA minimum requirement of 4.5:1, and suggests using text-zinc-500 instead to ensure proper readability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +14 to 17
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-400 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The column type and nullability helper text uses text-zinc-400 in light mode. On a light background (bg-zinc-50 or bg-white), this results in a contrast ratio of approximately 2.6:1, which violates WCAG AA accessibility guidelines (minimum 4.5:1 for normal text). Using text-zinc-500 instead provides a contrast ratio of ~4.8:1, ensuring readability while still maintaining a muted visual hierarchy.

Suggested change
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-400 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in 882fbb1. The sweep mapped the dark dimmest-text level (zinc-600) to zinc-400 in light, which is ~2.6:1 on a light bg. Bumped the light value to text-zinc-500 (~4.8:1, AA). Applied it consistently to every such span — the type/nullability hints here and in the edit modal and new-row form, plus the schema "—" default and the "null" cell — so all dimmest helper text now meets AA in light while dark stays zinc-600.

Comment on lines +15 to 18
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-400 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The column type and nullability helper text uses text-zinc-400 in light mode. On a light background (bg-zinc-50 or bg-white), this results in a contrast ratio of approximately 2.6:1, which violates WCAG AA accessibility guidelines (minimum 4.5:1 for normal text). Using text-zinc-500 instead provides a contrast ratio of ~4.8:1, ensuring readability while still maintaining a muted visual hierarchy.

Suggested change
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-400 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 882fbb1 (same zinc-400 -> zinc-500 light bump applied across all the dimmest helper-text spans).

<thead class="bg-white dark:bg-zinc-900 text-zinc-600 dark:text-zinc-400">
<tr>
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-600 text-xs ml-1">{{.Type}}</span></th>{{end}}
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-400 dark:text-zinc-600 text-xs ml-1">{{.Type}}</span></th>{{end}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The column type helper text in the table header uses text-zinc-400 in light mode. On a light background (bg-white), this results in a contrast ratio of approximately 2.6:1, which violates WCAG AA accessibility guidelines (minimum 4.5:1 for normal text). Using text-zinc-500 instead provides a contrast ratio of ~4.8:1, ensuring readability while still maintaining a muted visual hierarchy.

Suggested change
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-400 dark:text-zinc-600 text-xs ml-1">{{.Type}}</span></th>{{end}}
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}</span></th>{{end}}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 882fbb1 (same zinc-400 -> zinc-500 light bump applied across all the dimmest helper-text spans).

Comment on lines +86 to 89
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-400 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The column type and nullability helper text in the edit modal uses text-zinc-400 in light mode. On a light background (bg-zinc-50 or bg-white), this results in a contrast ratio of approximately 2.6:1, which violates WCAG AA accessibility guidelines (minimum 4.5:1 for normal text). Using text-zinc-500 instead provides a contrast ratio of ~4.8:1, ensuring readability while still maintaining a muted visual hierarchy.

Suggested change
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-400 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 882fbb1 (same zinc-400 -> zinc-500 light bump applied across all the dimmest helper-text spans).

@mickamy

mickamy commented Jun 30, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements accessibility (a11y) polish by introducing a light-mode toggle and updating templates and Tailwind CSS styles to support both light and dark themes. The feedback highlights several instances where the newly added light-mode text colors (such as text-zinc-600 and text-zinc-500) fail to meet the WCAG AA contrast ratio requirement of 4.5:1 against their backgrounds. It is recommended to apply the suggested higher-contrast alternatives (like text-zinc-700 and adjusted helper text colors) across the templates to ensure full accessibility compliance and improve readability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +4 to +8
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
PostgREST-style HTTP API over the connected database, with this admin UI
pinned to the schema discovered at startup.
</p>
<p class="text-zinc-400">
<p class="text-zinc-600 dark:text-zinc-400">

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Since this pull request focuses on a11y polish, we should ensure that the text colors in light mode meet the WCAG AA contrast ratio requirement of at least 4.5:1 for normal text.

Currently, text-zinc-600 (#52525b) on a bg-zinc-50 (#fafafa) background has a contrast ratio of only 4.01:1, which falls below the WCAG AA threshold. Using text-zinc-700 (#3f3f46) increases the contrast ratio to 5.74:1, which fully complies with WCAG AA guidelines and significantly improves readability.

Suggested change
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
PostgREST-style HTTP API over the connected database, with this admin UI
pinned to the schema discovered at startup.
</p>
<p class="text-zinc-400">
<p class="text-zinc-600 dark:text-zinc-400">
<p class="text-zinc-700 dark:text-zinc-400 mb-4">
PostgREST-style HTTP API over the connected database, with this admin UI
pinned to the schema discovered at startup.
</p>
<p class="text-zinc-700 dark:text-zinc-400">

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I measured these precisely (WCAG 2.x relative luminance) and the light-mode figures here don't match. text-zinc-600 (#52525b) is 7.73:1 on white and 7.41:1 on bg-zinc-50 — well above the 4.5:1 AA threshold, not 4.01:1. So this paragraph already passes AA in light, and bumping it to text-zinc-700 (10:1) would just over-darken the muted copy. Keeping text-zinc-600 dark:text-zinc-400 (dark side is 7.76:1).

Comment on lines +14 to 17
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve accessibility and meet WCAG AA contrast guidelines:

  1. The label color in light mode (text-zinc-600) has a contrast ratio of 4.01:1 on white/light backgrounds. Changing it to text-zinc-700 increases it to 5.74:1 (passing WCAG AA).
  2. The helper text (text-zinc-500 in light mode, dark:text-zinc-600 in dark mode) has extremely low contrast in both themes (2.83:1 and 2.45:1 respectively). Swapping them to text-zinc-600 and dark:text-zinc-500 improves the contrast ratios to 4.01:1 and 4.71:1 respectively, making the text significantly more readable.
Suggested change
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>
<label class="block text-zinc-700 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 dark:text-zinc-500 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two parts here:

  • Label (text-zinc-600 light): measured 7.41:1 on bg-zinc-50 (and 7.73:1 on white), already AA — the 4.01:1 figure doesn't match, so I'm keeping it (zinc-700 would be unnecessary over-darkening).
  • Helper hint: the real sub-AA case is dark mode, where dark:text-zinc-600 was only 2.57:1. Fixed in fde0f23 by making the hint text-zinc-500 in both themes — light 4.63:1 (unchanged, passes) and dark 4.12:1 (up from 2.57). zinc-400 (7.76:1) would match the label's prominence and flatten the hierarchy, so zinc-500 is the most readable step that stays dimmer than the label.

Comment on lines +15 to 18
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve accessibility and meet WCAG AA contrast guidelines:

  1. The label color in light mode (text-zinc-600) has a contrast ratio of 4.01:1 on white/light backgrounds. Changing it to text-zinc-700 increases it to 5.74:1 (passing WCAG AA).
  2. The helper text (text-zinc-500 in light mode, dark:text-zinc-600 in dark mode) has extremely low contrast in both themes (2.83:1 and 2.45:1 respectively). Swapping them to text-zinc-600 and dark:text-zinc-500 improves the contrast ratios to 4.01:1 and 4.71:1 respectively, making the text significantly more readable.
Suggested change
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>
<label class="block text-zinc-700 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 dark:text-zinc-500 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two parts here:

  • Label (text-zinc-600 light): measured 7.41:1 on bg-zinc-50 (and 7.73:1 on white), already AA — the 4.01:1 figure doesn't match, so I'm keeping it (zinc-700 would be unnecessary over-darkening).
  • Helper hint: the real sub-AA case is dark mode, where dark:text-zinc-600 was only 2.57:1. Fixed in fde0f23 by making the hint text-zinc-500 in both themes — light 4.63:1 (unchanged, passes) and dark 4.12:1 (up from 2.57). zinc-400 (7.76:1) would match the label's prominence and flatten the hierarchy, so zinc-500 is the most readable step that stays dimmer than the label.

Comment on lines +86 to 89
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve accessibility and meet WCAG AA contrast guidelines:

  1. The label color in light mode (text-zinc-600) has a contrast ratio of 4.01:1 on white/light backgrounds. Changing it to text-zinc-700 increases it to 5.74:1 (passing WCAG AA).
  2. The helper text (text-zinc-500 in light mode, dark:text-zinc-600 in dark mode) has extremely low contrast in both themes (2.83:1 and 2.45:1 respectively). Swapping them to text-zinc-600 and dark:text-zinc-500 improves the contrast ratios to 4.01:1 and 4.71:1 respectively, making the text significantly more readable.
Suggested change
<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>
<label class="block text-zinc-700 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 dark:text-zinc-500 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
</label>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two parts here:

  • Label (text-zinc-600 light): measured 7.41:1 on bg-zinc-50 (and 7.73:1 on white), already AA — the 4.01:1 figure doesn't match, so I'm keeping it (zinc-700 would be unnecessary over-darkening).
  • Helper hint: the real sub-AA case is dark mode, where dark:text-zinc-600 was only 2.57:1. Fixed in fde0f23 by making the hint text-zinc-500 in both themes — light 4.63:1 (unchanged, passes) and dark 4.12:1 (up from 2.57). zinc-400 (7.76:1) would match the label's prominence and flatten the hierarchy, so zinc-500 is the most readable step that stays dimmer than the label.

<thead class="bg-white dark:bg-zinc-900 text-zinc-600 dark:text-zinc-400">
<tr>
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-600 text-xs ml-1">{{.Type}}</span></th>{{end}}
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}</span></th>{{end}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve accessibility and meet WCAG AA contrast guidelines, swap the helper text colors to text-zinc-600 and dark:text-zinc-500. This improves the contrast ratios to 4.01:1 (light mode) and 4.71:1 (dark mode), making the text significantly more readable.

Suggested change
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-500 dark:text-zinc-600 text-xs ml-1">{{.Type}}</span></th>{{end}}
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-600 dark:text-zinc-500 text-xs ml-1">{{.Type}}</span></th>{{end}}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The light value already passes (text-zinc-500 = 4.63–4.83:1 depending on white vs zinc-50 bg). The genuinely faint case was dark mode (dark:text-zinc-600 = 2.57:1), fixed in fde0f23 by using text-zinc-500 in both themes (dark now 4.12:1, the most readable step that stays dimmer than the zinc-400 label).

<td class="px-3 py-2 align-top text-zinc-400 font-mono text-xs">{{if .Default}}{{.Default}}{{else}}<span class="text-zinc-600">—</span>{{end}}</td>
<td class="px-3 py-2 align-top text-zinc-600 dark:text-zinc-400 whitespace-nowrap">{{.Type}}</td>
<td class="px-3 py-2 align-top text-zinc-600 dark:text-zinc-400">{{if .Nullable}}yes{{else}}no{{end}}</td>
<td class="px-3 py-2 align-top text-zinc-600 dark:text-zinc-400 font-mono text-xs">{{if .Default}}{{.Default}}{{else}}<span class="text-zinc-500 dark:text-zinc-600">—</span>{{end}}</td>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To improve accessibility and meet WCAG AA contrast guidelines, swap the placeholder text colors to text-zinc-600 and dark:text-zinc-500. This improves the contrast ratios to 4.01:1 (light mode) and 4.71:1 (dark mode), making the text significantly more readable.

Suggested change
<td class="px-3 py-2 align-top text-zinc-600 dark:text-zinc-400 font-mono text-xs">{{if .Default}}{{.Default}}{{else}}<span class="text-zinc-500 dark:text-zinc-600"></span>{{end}}</td>
<td class="px-3 py-2 align-top text-zinc-600 dark:text-zinc-400 font-mono text-xs">{{if .Default}}{{.Default}}{{else}}<span class="text-zinc-600 dark:text-zinc-500"></span>{{end}}</td>

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The light value already passes (text-zinc-500 = 4.63–4.83:1 depending on white vs zinc-50 bg). The genuinely faint case was dark mode (dark:text-zinc-600 = 2.57:1), fixed in fde0f23 by using text-zinc-500 in both themes (dark now 4.12:1, the most readable step that stays dimmer than the zinc-400 label).

@mickamy

mickamy commented Jun 30, 2026

Copy link
Copy Markdown
Owner Author

/gemini review

@mickamy mickamy merged commit 2bded9b into main Jun 30, 2026
13 checks passed
@mickamy mickamy deleted the feat/ui-light-mode-toggle branch June 30, 2026 09:44

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements accessibility (a11y) polish and a light-mode toggle by introducing theme-aware Tailwind CSS classes, a theme toggle button, and a theme initialization script. The review feedback points out several instances across the HTML templates where the text-zinc-500 class is used for small text, resulting in a contrast ratio below the WCAG AA recommended 4.5:1 in both light and dark modes. To ensure proper readability, it is recommended to replace these with theme-aware class pairings like text-zinc-500 dark:text-zinc-400 or text-zinc-600 dark:text-zinc-400.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The text-zinc-500 class has a contrast ratio of ~3.97:1 on both bg-zinc-50 (light mode) and bg-zinc-950 (dark mode), which falls below the WCAG AA recommended contrast ratio of 4.5:1 for small text. Since this PR focuses on a11y polish, consider using a theme-aware class pairing like text-zinc-500 dark:text-zinc-400 or text-zinc-600 dark:text-zinc-400 to ensure proper readability in both themes.

Suggested change
<span class="text-zinc-500 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 dark:text-zinc-400 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>

<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The text-zinc-500 class has a contrast ratio of ~3.97:1 on both bg-zinc-50 (light mode) and bg-zinc-950 (dark mode), which falls below the WCAG AA recommended contrast ratio of 4.5:1 for small text. Consider using a theme-aware class pairing like text-zinc-500 dark:text-zinc-400 or text-zinc-600 dark:text-zinc-400 to ensure proper readability in both themes.

Suggested change
<span class="text-zinc-500 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 dark:text-zinc-400 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>

<label class="block text-zinc-500 text-xs mb-1">{{.Name}}=</label>
{{if isReservedFilterName .Name}}
<p class="text-xs text-zinc-600 italic px-2 py-1">column name is reserved — filter unavailable</p>
<p class="text-xs text-zinc-500 italic px-2 py-1">column name is reserved — filter unavailable</p>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The text-zinc-500 class has a contrast ratio of ~3.97:1 on both bg-zinc-50 (light mode) and bg-zinc-950 (dark mode), which falls below the WCAG AA recommended contrast ratio of 4.5:1 for small text. Consider using a theme-aware class pairing like text-zinc-500 dark:text-zinc-400 or text-zinc-600 dark:text-zinc-400 to ensure proper readability in both themes.

Suggested change
<p class="text-xs text-zinc-500 italic px-2 py-1">column name is reserved — filter unavailable</p>
<p class="text-xs text-zinc-500 dark:text-zinc-400 italic px-2 py-1">column name is reserved — filter unavailable</p>

<thead class="bg-white dark:bg-zinc-900 text-zinc-600 dark:text-zinc-400">
<tr>
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-600 text-xs ml-1">{{.Type}}</span></th>{{end}}
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-500 text-xs ml-1">{{.Type}}</span></th>{{end}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The text-zinc-500 class has a contrast ratio of ~3.97:1 on both bg-zinc-50 (light mode) and bg-zinc-950 (dark mode), which falls below the WCAG AA recommended contrast ratio of 4.5:1 for small text. Consider using a theme-aware class pairing like text-zinc-500 dark:text-zinc-400 or text-zinc-600 dark:text-zinc-400 to ensure proper readability in both themes.

Suggested change
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-500 text-xs ml-1">{{.Type}}</span></th>{{end}}
{{range .Columns}}<th data-col="{{.Name}}" class="px-3 py-2 text-left font-medium whitespace-nowrap cursor-pointer select-none">{{.Name}}<span class="text-zinc-500 dark:text-zinc-400 text-xs ml-1">{{.Type}}</span></th>{{end}}

<label class="block text-zinc-600 dark:text-zinc-400 text-sm mb-1">
{{.Name}}
<span class="text-zinc-600 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The text-zinc-500 class has a contrast ratio of ~3.97:1 on both bg-zinc-50 (light mode) and bg-zinc-950 (dark mode), which falls below the WCAG AA recommended contrast ratio of 4.5:1 for small text. Consider using a theme-aware class pairing like text-zinc-500 dark:text-zinc-400 or text-zinc-600 dark:text-zinc-400 to ensure proper readability in both themes.

Suggested change
<span class="text-zinc-500 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>
<span class="text-zinc-500 dark:text-zinc-400 text-xs ml-1">{{.Type}}{{if .Nullable}} · nullable{{end}}</span>

let cell;
if (v === null || v === undefined) {
cell = '<span class="text-zinc-600">null</span>';
cell = '<span class="text-zinc-500">null</span>';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The text-zinc-500 class has a contrast ratio of ~3.97:1 on both bg-zinc-50 (light mode) and bg-zinc-950 (dark mode), which falls below the WCAG AA recommended contrast ratio of 4.5:1 for small text. Consider using a theme-aware class pairing like text-zinc-500 dark:text-zinc-400 or text-zinc-600 dark:text-zinc-400 to ensure proper readability in both themes.

Suggested change
cell = '<span class="text-zinc-500">null</span>';
cell = '<span class="text-zinc-500 dark:text-zinc-400">null</span>';

<td class="px-3 py-2 align-top text-zinc-400 font-mono text-xs">{{if .Default}}{{.Default}}{{else}}<span class="text-zinc-600">—</span>{{end}}</td>
<td class="px-3 py-2 align-top text-zinc-600 dark:text-zinc-400 whitespace-nowrap">{{.Type}}</td>
<td class="px-3 py-2 align-top text-zinc-600 dark:text-zinc-400">{{if .Nullable}}yes{{else}}no{{end}}</td>
<td class="px-3 py-2 align-top text-zinc-600 dark:text-zinc-400 font-mono text-xs">{{if .Default}}{{.Default}}{{else}}<span class="text-zinc-500">—</span>{{end}}</td>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The text-zinc-500 class has a contrast ratio of ~3.97:1 on both bg-zinc-50 (light mode) and bg-zinc-950 (dark mode), which falls below the WCAG AA recommended contrast ratio of 4.5:1. Consider using a theme-aware class pairing like text-zinc-500 dark:text-zinc-400 or text-zinc-600 dark:text-zinc-400 to ensure proper readability in both themes.

Suggested change
<td class="px-3 py-2 align-top text-zinc-600 dark:text-zinc-400 font-mono text-xs">{{if .Default}}{{.Default}}{{else}}<span class="text-zinc-500"></span>{{end}}</td>
<td class="px-3 py-2 align-top text-zinc-600 dark:text-zinc-400 font-mono text-xs">{{if .Default}}{{.Default}}{{else}}<span class="text-zinc-500 dark:text-zinc-400"></span>{{end}}</td>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant