style(tasks-ui): retune the page to the plugin's own visual language

Stock shadcn defaults are designed for a standalone app; next to this plugin's
other tabs (files, changes, side chat, settings) they read as foreign: 14px body
against 12px, 36px controls against 28px, #00000029 borders against the plugin's
10%/4% hairlines, solid primary badges against flat state tints, and elevation
where every other panel is flat.

Measured the plugin's own vocabulary from its CSS modules (12px body / 11px meta
/ 10px micro; 16-20px line heights; 28px controls; 6x8 and 6x10 paddings; 8/6/4
gaps; 6/8px radii; `border-l2` for 1px borders and `border-l1` for hairlines;
`--dsw-shadow-lv2/3` only on floating layers) and mapped the vendored set onto
it instead of editing every page:

- theme.css bridge: `--border`/`--input` -> `border-l2` (l4 was visibly darker
  than every other tab), new `--border-subtle` -> `border-l1`, the type scale
  remapped (text-xs 11px, text-sm 12px, text-base 13px, text-lg 14px), elevation
  mapped to the plugin's tokens (static shadows none, md/lg -> --dsw-shadow-lv2
  /lv3), and the state tints bridged.
- component defaults retuned to the same scale: button sizes 28/24/20px with
  text-xs, card padding 12px with gap-3 and no shadow, input 28px, badge 18px
  with 11px ink, empty at p-4, separators on the hairline token.
- badges: the default variant is the plugin's flat state tint
  (bg-state-business-tertiary + state ink, the same recipe sidebar.module.css
  uses for revealed rows) rather than a solid primary fill.

Guards updated with the reasoning inline: the border bridge expectations, a
`none` value being the plugin's way of saying "flat", and the skin-contract
check now derives local token names from the file instead of a hand-kept list.

Verified: typecheck/lint clean; 136 files / 1515 tests; build with the bundle
guards; mount e2e 7/7; mirror-page screenshots (real host stylesheet + real
components) re-shot at 360/720 for graph, tree, board and the 77-node fixture.
This commit is contained in:
Menghuan1918
2026-09-27 22:18:19 +08:00
parent 5deb5285da
commit e500a3d2b9
8 changed files with 71 additions and 23 deletions
+10 -2
View File
@@ -47,8 +47,10 @@ const TOKEN_BRIDGE: Array<[shadcn: string, dsw: string]> = [
['--accent-foreground', '--dsw-alias-label-primary'],
['--destructive', '--dsw-alias-state-error-primary'],
['--destructive-foreground', '--dsw-alias-label-primary-foreground'],
['--border', '--dsw-alias-border-l4'],
['--input', '--dsw-alias-border-l4'],
// The plugin's own vocabulary: 1px borders are border-l2 (10%), hairlines
// border-l1 (4%). l4 is noticeably darker than every other tab's chrome.
['--border', '--dsw-alias-border-l2'],
['--input', '--dsw-alias-border-l2'],
['--ring', '--dsw-alias-state-business-primary'],
['--success', '--dsw-alias-state-success-primary'],
['--warning', '--dsw-alias-state-warn-primary'],
@@ -154,6 +156,9 @@ describe('tailwind entry: theme + utilities only', () => {
// scale — never a paint value of its own.
const definedElsewhere = new Set([
...TOKEN_BRIDGE.map(([name]) => name.slice(2)),
// Non-bridged locals the bridge forwards to (e.g. `--border-subtle`),
// read straight out of the file so they cannot drift out of the set.
...[...withoutComments.matchAll(/^\s*--([a-z0-9-]+)\s*:/gm)].map(m => m[1]),
'radius', 'radius-sm', 'radius-md', 'radius-lg', 'radius-xl',
])
for (const declaration of withoutComments.matchAll(/^\s*(--[a-z0-9-]+):\s*([^;]+);/gm)) {
@@ -163,6 +168,9 @@ describe('tailwind entry: theme + utilities only', () => {
const refName = reference?.[1]
const ok = (refName !== undefined && (refName.startsWith('dsw-') || definedElsewhere.has(refName)))
|| /^[0-9.]+(?:px|rem|em|%)$/.test(value)
// `none` is how the plugin says "flat": static panels take no elevation
// (the floating layers map to the host's --dsw-shadow-lv* tokens).
|| value === 'none'
expect(ok, `${name}: ${value}`).toBe(true)
}
})