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:
@@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user