Skip to content

feat(frontend): move the dashboard to Tailwind CSS v4 and rebuild it around limits - #10

Merged
TheMeinerLP merged 2 commits into
mainfrom
feat/tailwind-dashboard-ui
Aug 8, 2026
Merged

feat(frontend): move the dashboard to Tailwind CSS v4 and rebuild it around limits#10
TheMeinerLP merged 2 commits into
mainfrom
feat/tailwind-dashboard-ui

Conversation

@TheMeinerLP

@TheMeinerLP TheMeinerLP commented Aug 8, 2026

Copy link
Copy Markdown

Description

Two changes to the dashboard, kept as two commits because they answer different questions and carry different risk.

build(frontend): WindiCSS out, Tailwind CSS v4 in. Windi has been unmaintained since 2023 and vite-plugin-windicss was the only thing still holding the dashboard on it. The migration is deliberately mechanical: src/style.css carries the theme that used to live in windi.config.js, and a clearly marked compatibility block below it reproduces what Windi emitted, so this commit changes the toolchain and nothing a user sees.

Six differences needed handling rather than translation, and each is commented where it lands:

What Effect if missed
Component <style> blocks sit outside every cascade layer .container { @apply px-10 } outranked the px-15 several views write beside it, shifting whole pages by 20px
Tailwind accepts spacing only in multiples of 0.25 Nine classes such as pt-0.4 silently stopped resolving
Windi shipped an indent utility of its own The token routes have a component class of the same name and were quietly getting text-indent: 1.5rem on top of their padding
:where in the dark variant contributes no specificity The tab indicator from vue3-tabs won and lost its colour in dark mode
@tailwindcss/forms styles input:not([type]), Windi's older port did not Thirteen inputs were laid out against the untouched browser box
dark: bg-gray-900 (with a space) in the settings styles A typo Windi swallowed and Tailwind rejects outright

feat(frontend): the dashboard rebuilt around limits and single measures. The Resources chart drew used memory in megabytes and the thread count on one y-axis. Whichever number happened to be larger got the taller line, so the comparison a reader took from that shape meant nothing. They are two charts now on a shared time axis.

The dashboard before and after, light theme at 1440px

The rest follows from the same question, what does an operator need to read off this page in ten seconds:

  • Memory and threads are shares of a limit, so they get a meter instead of 151.5 of 32 MB as running text. Amber at three quarters, red at nine tenths.
  • latestVersion has been in the status response all along and nothing used it. The version tile now says whether a newer release exists.
  • The always green dot beside the heading is gone. Failures carry the state, and every state ships with a word, never colour alone.
  • Series colours come from a palette validated against both card surfaces for colour vision separation and contrast, instead of eight hand picked hues. Multi series charts draw lines rather than stacked translucent areas that hid each other, and fold everything past the eighth repository into one series.
  • Loading and empty states. The resolved requests card used to vanish entirely when statistics were off, leaving a gap where a reader could not tell a disabled feature from a failed request.
  • Instance status polled once a second and kept polling behind a hidden tab. Five seconds now, paused while hidden, stopped by unmounting instead of by checking which tab is selected.
  • The tab bar on small screens: five tabs pinned to a quarter of the width each wrapped onto a second line and cut off the last label. One line, scrolls.

The tab bar at 390px, before and after

Dark theme and 390px

The rebuilt dashboard in the dark theme

The rebuilt dashboard at 390px

The stub backend grew memory past maxMemory without bound and returned two snapshots, which made the new meters and trends impossible to judge while working on them.

All screenshots come from fake-api/fake-ingot-backend.js, so the numbers are stub data. They live on the orphan branch assets/pr-10-screenshots, which shares no history with main and is never merged, so the repository does not carry the PNGs through every clone. Deleting that branch breaks the images above.

No backend, configuration, container or plugin surface is touched. ApexCharts is not involved: it was already replaced by Apache ECharts in dfd70d66.

Type of change

  • feat - new feature
  • build / ci / chore - tooling, dependencies, pipelines

How was this tested?

The frontend has no automated test suite, so the migration was verified by rendering both versions and comparing them.

Both versions were run side by side, the pre-migration tree from a second worktree on port 8899 and this branch on 8888, driven by the same Playwright script: five views times two themes times two viewports, 20 screenshots per version, compared per pixel with ImageMagick.

Result after the six fixes above:

overview-light-desktop      0
overview-dark-desktop       0
settings-light-desktop      0
settings-dark-desktop       0
tokens-light-desktop        0
tokens-dark-desktop         0
console-dark-desktop        0

Every remaining difference was traced rather than waved through: toast progress bars mid animation, live data (uptime, chart series, console log lines), and one accepted change, a <select> now inherits the interface font instead of falling back to Arial, which is a fix.

Beyond the screenshots:

  • All 97 component class names were checked against Windi's utility list to find name collisions. Two existed, indent (visible, restored explicitly and commented) and tab (emitted only tab-size, no effect).
  • Every class token in the templates was checked against the generated CSS to catch utilities that stopped resolving without a build error.
  • The chart palette was run through a colour vision and contrast validator against both card surfaces, light and dark, before being adopted.
  • Both commits build in isolation, not just the branch tip.
  • npm run eslint: 7 errors, identical to main. All of them predate this branch and sit in src/store/console/connection.js.
  • ./gradlew build passes.

Checklist

  • Commit messages follow the Conventional Commits convention documented in CLAUDE.md
  • Tests cover the change, or the change is not testable and I explained why above
  • ./gradlew build passes locally
  • Documentation and configuration samples are updated if the change is user-facing
  • Existing copyright headers are preserved; new files carry the appropriate header
  • No credentials, tokens or personal data are included in the diff

On the tests box: the dashboard has no test infrastructure at all and this branch does not add one, so the box stays unticked rather than being explained away. The verification above is a screenshot comparison against the previous implementation, which is reproducible but not committed. If visual regression tests in CI are wanted, that is worth its own PR, and this branch would be a good baseline to start from.

WindiCSS has been unmaintained since 2023, and its Vite plugin is the
only thing still holding the dashboard on it. Tailwind v4 covers the
same ground through @tailwindcss/vite and is a maintained dependency
again.

The move is deliberately mechanical, so the diff reads as a toolchain
change and nothing else. src/style.css carries the theme that used to
live in windi.config.js, and a compatibility block below it reproduces
what Windi emitted. That block is scaffolding for the UI work that
follows and is marked for deletion.

Six things needed handling rather than translation:

- Component style blocks sit outside every cascade layer, which ranked
  .container above the px-15 utilities several views write beside it.
  The global rules moved out of App.vue into the layer each one needs.
- Tailwind accepts spacing only in multiples of 0.25, so nine classes
  such as pt-0.4 stopped resolving. They are explicit rem values now.
- Windi shipped an `indent` utility of its own, which silently added a
  text-indent to the token routes, whose component class has the same
  name.
- The dark variant uses :is and not :where, because :where adds no
  specificity and the indicator vue3-tabs styles then wins.
- @tailwindcss/forms styles an input written without a type, which
  Windi's older port did not. Thirteen inputs were laid out against
  the untouched browser box.
- `dark: bg-gray-900` in the settings styles was a typo that Windi
  swallowed and Tailwind rejects.

Verified by running both versions side by side and comparing every
view in both themes at two widths: the desktop views outside the
dashboard come out pixel identical. One difference is kept on purpose,
a select now inherits the interface font instead of falling back to
Arial.

Signed-off-by: TheMeinerLP <github@themeinerlp.dev>
The Resources chart drew used memory in megabytes and the thread count
on one y-axis. Whichever number happened to be larger got the taller
line, so the comparison a reader took from that shape meant nothing.
Memory and threads are two charts now, side by side on a shared time
axis: the trends stay comparable and the magnitudes stop pretending to
be.

The rest follows from the same question, what does an operator need to
read off this page in ten seconds:

- Memory and threads are shares of a limit, so they get a meter rather
  than "151.5 of 32 MB" as running text, and the meter turns amber at
  three quarters and red at nine tenths.
- latestVersion has been in the status response all along and nothing
  on this page used it. The version tile now says whether a newer
  release exists.
- The always green dot beside the heading is gone. Failures carry the
  state instead, and every state ships with a word, never colour on
  its own.
- Series colours come from a palette checked against both card
  surfaces for colour-vision separation and contrast, instead of eight
  hand-picked hues. Multi-series charts draw lines rather than stacked
  translucent areas, which hid each other, and fold everything past
  the eighth repository into one series.
- Loading and empty states. The resolved requests card used to vanish
  entirely when statistics were switched off, leaving a gap where a
  reader had no way to tell a disabled feature from a failed request.
- Instance status polled once a second and kept polling behind a
  hidden tab. It asks every five seconds now, pauses while the tab is
  hidden, and stops by unmounting rather than by checking which tab is
  selected.

Also fixes the tab bar on small screens, where five tabs pinned to a
quarter of the width each wrapped onto a second line and cut off the
last label. It stays on one line and scrolls.

The stub backend grew memory past maxMemory without bound and returned
two snapshots, which left the new meters and trends impossible to
judge while working on them.

Signed-off-by: TheMeinerLP <github@themeinerlp.dev>
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

Test results

 68 files   68 suites   12m 39s ⏱️
325 tests 324 ✅ 1 💤 0 ❌
327 runs  326 ✅ 1 💤 0 ❌

Results for commit 6d46bfc.

@TheMeinerLP
TheMeinerLP merged commit d137bd3 into main Aug 8, 2026
8 checks passed
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