fix: simplify workspace borders and design rules

This commit is contained in:
2026-09-18 04:25:39 +09:00
parent 272cd1bb03
commit 520c2f70c8
11 changed files with 206 additions and 670 deletions
+52 -73
View File
@@ -3,7 +3,7 @@ function assert(condition: unknown, message: string): asserts condition {
if (!condition) throw new Error(message);
}
Deno.test("Bevel keeps depth visual while semantic controls remain children", async () => {
Deno.test("Bevel draws a plain selectable border around semantic children", async () => {
const source = await Deno.readTextFile(
new URL("../src/lib/workspace/ui/Bevel.svelte", import.meta.url),
);
@@ -19,97 +19,71 @@ Deno.test("Bevel keeps depth visual while semantic controls remain children", as
for (
const contract of [
"export type BevelProfile = 'edge' | 'ridge'",
"export type BevelDepth = 'raised' | 'inset'",
"children: Snippet<[]>",
"top?: boolean",
"right?: boolean",
"bottom?: boolean",
"left?: boolean",
"pointer-events: none",
"data-pressed={pressed || undefined}",
"data-invalid={invalid || undefined}",
"border-style: solid",
"border-color: var(--line-strong)",
"border-width: 1px",
"aria-invalid='true'",
"forced-colors: active",
"mask-composite: exclude",
]
) {
assert(source.includes(contract), `Bevel must preserve ${contract}`);
}
assert(
source.includes("@supports") &&
source.includes("border-style: solid") &&
source.includes(
"border-color: var(--_fallback-border, var(--line-strong))",
) &&
source.includes("--_fallback-border: var(--line)") &&
source.includes("--_fallback-border: var(--line-strong)") &&
source.includes(
"border-width: var(--_top-w) var(--_right-w) var(--_bottom-w) var(--_left-w)",
),
"Bevel fallback colors must not outrank the enhanced transparent border",
);
assert(
source.includes("calc(max(cos(180deg - var(--_L)), 0) * 100%)") &&
source.includes("var(--_s6) 90deg") &&
source.includes("var(--_s6) 270deg") &&
!source.includes("+ 1) * 50%"),
"Bevel must clamp unlit faces to the configured shadow without ambient light",
);
assert(
appCss.includes("--bevel-face-width: 1px") &&
source.includes("--_W: var(--bevel-face-width)") &&
source.includes("inset: var(--bevel-face-width)") &&
!source.includes("BevelSize") &&
!source.includes("data-size"),
"Bevel must use the project-wide 1px edge and compose ridge from two 1px faces",
);
assert(
source.includes("data-top={top ? 'true' : 'false'}") &&
source.includes("data-right={right ? 'true' : 'false'}") &&
source.includes("data-bottom={bottom ? 'true' : 'false'}") &&
source.includes("data-left={left ? 'true' : 'false'}") &&
source.includes(".bevel[data-top='false'] {") &&
source.includes("border-top-width: 0") &&
source.includes("border-right-width: 0") &&
source.includes("border-bottom-width: 0") &&
source.includes("border-left-width: 0") &&
source.includes("--bevel-top-left-radius: 0px") &&
source.includes("--bevel-top-right-radius: 0px") &&
source.includes(".bevel[data-right='false'] {") &&
source.includes("--bevel-bottom-right-radius: 0px") &&
source.includes(".bevel[data-bottom='false'] {") &&
source.includes("--bevel-bottom-left-radius: 0px") &&
source.includes(".bevel[data-left='false'] {") &&
source.includes("--bevel-top-inset: 0px") &&
source.includes("--bevel-right-inset: 0px") &&
source.includes("--bevel-bottom-inset: 0px") &&
source.includes("--bevel-left-inset: 0px"),
"Bevel must draw selected sides and round only corners shared by two enabled sides",
source.includes("--bevel-bottom-right-radius: 0px"),
"Bevel must draw only selected sides and round only corners shared by enabled sides",
);
assert(
source.includes("data-profile={profile}") &&
source.includes("[data-profile='ridge'][data-depth='raised']::after") &&
source.includes("[data-profile='ridge'][data-depth='inset']::after") &&
!source.includes("data-depth='ridge'"),
"Bevel must keep edge/ridge profile orthogonal to raised/inset depth",
!source.includes("BevelProfile") &&
!source.includes("BevelDepth") &&
!source.includes("profile") &&
!source.includes("depth") &&
!source.includes("pressed") &&
!source.includes("::before") &&
!source.includes("::after") &&
!source.includes("gradient") &&
!source.includes("mask") &&
!source.includes("color-mix") &&
!source.includes("cos(") &&
!source.includes("pow(") &&
!appCss.includes("--bevel-highlight") &&
!appCss.includes("--bevel-shadow") &&
!appCss.includes("--bevel-highlight-curve") &&
!appCss.includes("--bevel-face-width"),
"Bevel must remain a plain border without lighting or depth simulation",
);
assert(
appCss.includes("--bevel-highlight: #fff") &&
appCss.includes("--bevel-shadow: #000") &&
!source.includes("surface") &&
!source.includes("tone") &&
!source.includes("background-color:"),
"Bevel must use the shared highlight and shadow palette without owning a surface tone",
!source.includes("background:") &&
!source.includes("background-color:") &&
source.includes("button, input, select, textarea"),
"Bevel must not own surface color and must keep semantic controls as children",
);
assert(
designLanguage.includes("Bevel自体へbackgroundやsurface toneを与えず") &&
designLanguage.includes("1pxの単色borderを描くvisual wrapper") &&
designLanguage.includes(
"照明、raised/inset、ridge/grooveを表現しない",
) &&
designLanguage.includes("text/content areaへ適用する"),
"The design language must prohibit Bevel surface colors outside the child content area",
);
assert(
source.includes("button, input, select, textarea"),
"Bevel must leave native semantic controls inside the visual wrapper",
"The design language must define Bevel as a plain border without surface ownership",
);
});
Deno.test("Workspace showroom exercises raised inset and ridge edges", async () => {
Deno.test("Workspace showroom exercises closed and adjacent borders", async () => {
const workspaceSource = await Deno.readTextFile(
new URL(
"../src/routes/design-lab/workspace-web-ux/+page.svelte",
@@ -133,21 +107,26 @@ Deno.test("Workspace showroom exercises raised inset and ridge edges", async ()
)?.[1];
assert(
workspaceSource.includes('profile="edge" depth="raised"') &&
workspaceSource.includes('profile="ridge" depth="raised"') &&
workspaceSource.includes('profile="ridge" depth="inset"') &&
workspaceSource.includes('<div class="border-matrix">') &&
workspaceSource.includes("right={false} bottom={false}") &&
workspaceSource.includes("top={false} left={false}") &&
workspaceSource.includes("pressed"),
"Workspace showroom must demonstrate edge and ridge depths with full and adjacent side selections",
workspaceSource.includes("Closed border") &&
workspaceSource.includes("Read-only border") &&
!workspaceSource.includes("profile=") &&
!workspaceSource.includes("depth=") &&
!workspaceSource.includes("<Bevel pressed"),
"Workspace showroom must demonstrate closed and adjacent plain borders",
);
assert(
tactileGroupRule && !tactileGroupRule.includes("background"),
"Showroom ridge items must leave their content area background unset",
"Showroom bordered items must leave their content area background unset",
);
assert(
settingsSource.includes('profile="edge" depth="inset"') &&
settingsSource.includes('aria-invalid="true"'),
"Settings showroom must demonstrate inset and invalid fields",
settingsSource.includes("<Bevel fill>") &&
settingsSource.includes("<Bevel fill invalid>") &&
settingsSource.includes('aria-invalid="true"') &&
!settingsSource.includes("profile=") &&
!settingsSource.includes("depth="),
"Settings showroom must demonstrate plain and invalid borders",
);
});
+24 -42
View File
@@ -7,9 +7,6 @@ Deno.test("BevelLine exposes a semantic directional separator", async () => {
const source = await Deno.readTextFile(
new URL("../src/lib/workspace/ui/BevelLine.svelte", import.meta.url),
);
const appCss = await Deno.readTextFile(
new URL("../src/app.css", import.meta.url),
);
for (
const contract of [
@@ -28,41 +25,25 @@ Deno.test("BevelLine exposes a semantic directional separator", async () => {
}
assert(
appCss.includes("--bevel-face-width: 1px") &&
source.includes("--bevel-line-face-width: var(--bevel-face-width)") &&
source.includes(
"--bevel-line-width: calc(var(--bevel-line-face-width) * 2)",
) &&
!source.includes("BevelLineWeight") &&
!source.includes("data-weight"),
"BevelLine must compose a fixed 2px ridge from two project-wide 1px faces",
source.includes("border-color: var(--line-strong)") &&
source.includes("border-top-style: solid") &&
source.includes("border-top-width: 1px") &&
source.includes("border-left-style: solid") &&
source.includes("border-left-width: 1px") &&
!source.includes("background:") &&
!source.includes("gradient") &&
!source.includes("color-mix") &&
!source.includes("BevelLineDepth") &&
!source.includes("depth"),
"BevelLine must remain a plain one-pixel directional border",
);
assert(
!source.includes("--bevel-line-inset") &&
!source.includes("padding-inline") &&
!source.includes("padding-block"),
!source.includes("padding-inline") &&
!source.includes("padding-block") &&
!source.includes("margin-inline") &&
!source.includes("margin-block"),
"BevelLine must fill its parent layer without independent endpoint spacing",
);
assert(
source.includes("background: var(--line-strong)") &&
source.includes("@supports") &&
source.includes("linear-gradient(to bottom") &&
source.includes("linear-gradient(to right"),
"BevelLine must retain a plain fallback and directional enhanced edges",
);
assert(
source.includes("var(--bevel-highlight)") &&
source.includes("var(--bevel-shadow)") &&
!source.includes("surface"),
"BevelLine must use the shared highlight and shadow palette without a surface color",
);
assert(
source.includes("calc(max(cos(180deg - var(--_L)), 0) * 100%)") &&
source.includes("var(--_s6) 90deg") &&
source.includes("var(--_s6) 270deg") &&
!source.includes("+ 1) * 50%"),
"BevelLine must clamp unlit faces to the configured shadow without ambient light",
);
});
Deno.test("Workspace showroom demonstrates both BevelLine directions", async () => {
@@ -76,13 +57,14 @@ Deno.test("Workspace showroom demonstrates both BevelLine directions", async ()
assert(
source.includes('direction="x" length="100%"') &&
source.includes('direction="y" length="72px"') &&
source.includes('direction="x" length="160px" depth="inset"') &&
source.includes('direction="x" length="160px"') &&
!source.includes("depth=") &&
!source.includes("weight="),
"Workspace showroom must demonstrate horizontal, vertical, and inset BevelLine states",
"Workspace showroom must demonstrate horizontal and vertical plain separators",
);
});
Deno.test("Shell regions use single-edge Bevel while open separators use BevelLine", async () => {
Deno.test("Shell regions use selected Bevel edges while open separators use BevelLine", async () => {
const rootLayout = await Deno.readTextFile(
new URL("../src/routes/+layout.svelte", import.meta.url),
);
@@ -110,21 +92,21 @@ Deno.test("Shell regions use single-edge Bevel while open separators use BevelLi
assert(
rootLayout.includes('class="app-shell__topbar-bevel"') &&
rootLayout.includes('depth="inset"') &&
rootLayout.includes("top={false} right={false} left={false}") &&
rootLayout.includes("bind:folded={sidebarFolded}") &&
rootLayout.includes("app-shell__mobile-sidebar-toggle") &&
rootLayout.includes("class:sidebar-open={!sidebarFolded}") &&
!rootLayout.includes("depth=") &&
!rootLayout.includes("BevelLine") &&
sidebarFrame.includes('class="sidebar-frame__bevel"') &&
sidebarFrame.includes('depth="inset"') &&
sidebarFrame.includes("top={false} bottom={false} left={false}") &&
sidebarFrame.includes("$bindable(false)") &&
!sidebarFrame.includes("depth=") &&
!sidebarFrame.includes("BevelLine") &&
sidebarCss.includes(".sidebar-frame.folded") &&
sidebarCss.includes("display: none") &&
sidebarCss.includes("display: grid"),
"Desktop shell boundaries must use one Bevel edge and Mobile must share Header fold state",
"Desktop shell boundaries must use one selected border edge and Mobile must share Header fold state",
);
assert(
designLanguage.includes(
@@ -138,13 +120,13 @@ Deno.test("Shell regions use single-edge Bevel while open separators use BevelLi
) &&
showroomCss.includes(".resource-list {") &&
showroomCss.includes("margin-inline: var(--space-2)"),
"The design language and showroom must align same-layer lines and inset nested layers as a whole",
"The design language and showroom must preserve adjacent area and line hierarchy",
);
assert(
!sidebarCss.includes("border-right:") &&
!sidebarCss.includes("border-bottom:") &&
!showroomCss.includes("border-top: 1px solid") &&
!showroomCss.includes("border-bottom: 1px solid"),
"Shell and showroom structural boundaries must not fall back to one-sided solid borders",
"Shell and showroom structural boundaries must use shared Bevel components",
);
});