fix: simplify workspace borders and design rules
This commit is contained in:
@@ -49,9 +49,6 @@
|
||||
--danger: oklch(54% 0.14 25);
|
||||
--interactive-hover: oklch(94.5% 0 0);
|
||||
--interactive-selected: oklch(93% 0.02 230);
|
||||
--bevel-highlight: #fff;
|
||||
--bevel-shadow: #000;
|
||||
--bevel-face-width: 1px;
|
||||
--shadow-overlay: 0 12px 32px oklch(0% 0 0 / 0.14);
|
||||
|
||||
--font-sans: "Gen Interface JP", sans-serif;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
</script>
|
||||
|
||||
<div class="sidebar-frame" class:folded>
|
||||
<Bevel as="div" class="sidebar-frame__bevel" depth="inset" top={false} bottom={false} left={false} fill>
|
||||
<Bevel as="div" class="sidebar-frame__bevel" top={false} bottom={false} left={false} fill>
|
||||
<aside class="sidebar-frame__surface" aria-label="Sidebar">
|
||||
{#if !folded}
|
||||
<div class="sidebar-frame-content">
|
||||
|
||||
@@ -1,35 +1,26 @@
|
||||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
|
||||
export type BevelProfile = 'edge' | 'ridge';
|
||||
export type BevelDepth = 'raised' | 'inset';
|
||||
|
||||
type Props = {
|
||||
children: Snippet<[]>;
|
||||
profile?: BevelProfile;
|
||||
depth?: BevelDepth;
|
||||
top?: boolean;
|
||||
right?: boolean;
|
||||
bottom?: boolean;
|
||||
left?: boolean;
|
||||
fill?: boolean;
|
||||
invalid?: boolean;
|
||||
pressed?: boolean;
|
||||
as?: 'span' | 'div';
|
||||
class?: string;
|
||||
};
|
||||
|
||||
let {
|
||||
children,
|
||||
profile = 'edge',
|
||||
depth = 'raised',
|
||||
top = true,
|
||||
right = true,
|
||||
bottom = true,
|
||||
left = true,
|
||||
fill = false,
|
||||
invalid = false,
|
||||
pressed = false,
|
||||
as = 'span',
|
||||
class: className = '',
|
||||
}: Props = $props();
|
||||
@@ -38,35 +29,28 @@
|
||||
<svelte:element
|
||||
this={as}
|
||||
class={`bevel ${fill ? 'bevel--fill' : ''} ${className}`.trim()}
|
||||
data-profile={profile}
|
||||
data-depth={depth}
|
||||
data-top={top ? 'true' : 'false'}
|
||||
data-right={right ? 'true' : 'false'}
|
||||
data-bottom={bottom ? 'true' : 'false'}
|
||||
data-left={left ? 'true' : 'false'}
|
||||
data-invalid={invalid || undefined}
|
||||
data-pressed={pressed || undefined}
|
||||
>
|
||||
{@render children()}
|
||||
</svelte:element>
|
||||
|
||||
<style>
|
||||
.bevel {
|
||||
--bevel-light: 315deg;
|
||||
--bevel-radius: var(--radius-soft);
|
||||
--bevel-top-left-radius: var(--bevel-radius);
|
||||
--bevel-top-right-radius: var(--bevel-radius);
|
||||
--bevel-bottom-right-radius: var(--bevel-radius);
|
||||
--bevel-bottom-left-radius: var(--bevel-radius);
|
||||
--bevel-top-inset: var(--bevel-face-width);
|
||||
--bevel-right-inset: var(--bevel-face-width);
|
||||
--bevel-bottom-inset: var(--bevel-face-width);
|
||||
--bevel-left-inset: var(--bevel-face-width);
|
||||
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
display: inline-grid;
|
||||
min-width: 0;
|
||||
border-style: solid;
|
||||
border-color: var(--line-strong);
|
||||
border-width: 1px;
|
||||
border-radius:
|
||||
var(--bevel-top-left-radius)
|
||||
var(--bevel-top-right-radius)
|
||||
@@ -78,25 +62,29 @@
|
||||
.bevel[data-top='false'] {
|
||||
--bevel-top-left-radius: 0px;
|
||||
--bevel-top-right-radius: 0px;
|
||||
--bevel-top-inset: 0px;
|
||||
|
||||
border-top-width: 0;
|
||||
}
|
||||
|
||||
.bevel[data-right='false'] {
|
||||
--bevel-top-right-radius: 0px;
|
||||
--bevel-bottom-right-radius: 0px;
|
||||
--bevel-right-inset: 0px;
|
||||
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
.bevel[data-bottom='false'] {
|
||||
--bevel-bottom-right-radius: 0px;
|
||||
--bevel-bottom-left-radius: 0px;
|
||||
--bevel-bottom-inset: 0px;
|
||||
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
|
||||
.bevel[data-left='false'] {
|
||||
--bevel-bottom-left-radius: 0px;
|
||||
--bevel-top-left-radius: 0px;
|
||||
--bevel-left-inset: 0px;
|
||||
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.bevel--fill {
|
||||
@@ -104,8 +92,6 @@
|
||||
}
|
||||
|
||||
.bevel > :global(*) {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
min-width: 0;
|
||||
border-radius: inherit;
|
||||
}
|
||||
@@ -114,235 +100,30 @@
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.bevel::before,
|
||||
.bevel[data-profile='ridge']::after {
|
||||
--_W: var(--bevel-face-width);
|
||||
--_top-w: var(--_W);
|
||||
--_right-w: var(--_W);
|
||||
--_bottom-w: var(--_W);
|
||||
--_left-w: var(--_W);
|
||||
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
inset: 0;
|
||||
box-sizing: border-box;
|
||||
border-style: solid;
|
||||
border-color: var(--_fallback-border, var(--line-strong));
|
||||
border-width: var(--_top-w) var(--_right-w) var(--_bottom-w) var(--_left-w);
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.bevel[data-top='false']::before,
|
||||
.bevel[data-top='false'][data-profile='ridge']::after {
|
||||
--_top-w: 0px;
|
||||
}
|
||||
|
||||
.bevel[data-right='false']::before,
|
||||
.bevel[data-right='false'][data-profile='ridge']::after {
|
||||
--_right-w: 0px;
|
||||
}
|
||||
|
||||
.bevel[data-bottom='false']::before,
|
||||
.bevel[data-bottom='false'][data-profile='ridge']::after {
|
||||
--_bottom-w: 0px;
|
||||
}
|
||||
|
||||
.bevel[data-left='false']::before,
|
||||
.bevel[data-left='false'][data-profile='ridge']::after {
|
||||
--_left-w: 0px;
|
||||
}
|
||||
|
||||
.bevel[data-profile='edge'][data-depth='inset']::before {
|
||||
--_fallback-border: var(--line);
|
||||
}
|
||||
|
||||
.bevel[data-profile='ridge'][data-depth='inset']::before {
|
||||
--_fallback-border: var(--line);
|
||||
}
|
||||
|
||||
.bevel[data-profile='ridge']::after {
|
||||
inset:
|
||||
var(--bevel-top-inset)
|
||||
var(--bevel-right-inset)
|
||||
var(--bevel-bottom-inset)
|
||||
var(--bevel-left-inset);
|
||||
--_fallback-border: var(--line);
|
||||
border-radius:
|
||||
max(0px, calc(var(--bevel-top-left-radius) - var(--bevel-face-width)))
|
||||
max(0px, calc(var(--bevel-top-right-radius) - var(--bevel-face-width)))
|
||||
max(0px, calc(var(--bevel-bottom-right-radius) - var(--bevel-face-width)))
|
||||
max(0px, calc(var(--bevel-bottom-left-radius) - var(--bevel-face-width)));
|
||||
}
|
||||
|
||||
.bevel[data-profile='ridge'][data-depth='inset']::after {
|
||||
--_fallback-border: var(--line-strong);
|
||||
}
|
||||
|
||||
.bevel[data-invalid='true'],
|
||||
.bevel:has(> :global([aria-invalid='true'])) {
|
||||
box-shadow: 0 0 0 1px var(--danger);
|
||||
border-color: var(--danger);
|
||||
}
|
||||
|
||||
.bevel:has(> :global(:is(:disabled, [aria-disabled='true']))) {
|
||||
opacity: 0.58;
|
||||
}
|
||||
|
||||
@supports
|
||||
(color: color-mix(in oklab, white 50%, black)) and
|
||||
(width: calc(cos(0deg) * 1px)) and
|
||||
((mask-composite: exclude) or (-webkit-mask-composite: xor)) {
|
||||
.bevel {
|
||||
--_s0: var(--bevel-highlight);
|
||||
--_s1: color-mix(in oklab, var(--bevel-highlight) 96.5926%, var(--bevel-shadow));
|
||||
--_s2: color-mix(in oklab, var(--bevel-highlight) 86.6025%, var(--bevel-shadow));
|
||||
--_s3: color-mix(in oklab, var(--bevel-highlight) 70.7107%, var(--bevel-shadow));
|
||||
--_s4: color-mix(in oklab, var(--bevel-highlight) 50%, var(--bevel-shadow));
|
||||
--_s5: color-mix(in oklab, var(--bevel-highlight) 25.8819%, var(--bevel-shadow));
|
||||
--_s6: var(--bevel-shadow);
|
||||
--_shade:
|
||||
var(--_s0) 0deg,
|
||||
var(--_s1) 15deg,
|
||||
var(--_s2) 30deg,
|
||||
var(--_s3) 45deg,
|
||||
var(--_s4) 60deg,
|
||||
var(--_s5) 75deg,
|
||||
var(--_s6) 90deg,
|
||||
var(--_s6) 270deg,
|
||||
var(--_s5) 285deg,
|
||||
var(--_s4) 300deg,
|
||||
var(--_s3) 315deg,
|
||||
var(--_s2) 330deg,
|
||||
var(--_s1) 345deg,
|
||||
var(--_s0) 360deg;
|
||||
}
|
||||
|
||||
.bevel::before,
|
||||
.bevel[data-profile='ridge']::after {
|
||||
--_W: var(--bevel-face-width);
|
||||
--_RTL: var(--bevel-top-left-radius);
|
||||
--_RTR: var(--bevel-top-right-radius);
|
||||
--_RBR: var(--bevel-bottom-right-radius);
|
||||
--_RBL: var(--bevel-bottom-left-radius);
|
||||
--_L: var(--bevel-light);
|
||||
--_top: color-mix(
|
||||
in oklab,
|
||||
var(--bevel-highlight) calc(max(cos(0deg - var(--_L)), 0) * 100%),
|
||||
var(--bevel-shadow)
|
||||
);
|
||||
--_right: color-mix(
|
||||
in oklab,
|
||||
var(--bevel-highlight) calc(max(cos(90deg - var(--_L)), 0) * 100%),
|
||||
var(--bevel-shadow)
|
||||
);
|
||||
--_bottom: color-mix(
|
||||
in oklab,
|
||||
var(--bevel-highlight) calc(max(cos(180deg - var(--_L)), 0) * 100%),
|
||||
var(--bevel-shadow)
|
||||
);
|
||||
--_left: color-mix(
|
||||
in oklab,
|
||||
var(--bevel-highlight) calc(max(cos(270deg - var(--_L)), 0) * 100%),
|
||||
var(--bevel-shadow)
|
||||
);
|
||||
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-width: var(--_top-w) var(--_right-w) var(--_bottom-w) var(--_left-w);
|
||||
border-radius: var(--_RTL) var(--_RTR) var(--_RBR) var(--_RBL);
|
||||
background:
|
||||
conic-gradient(from var(--_L) at 100% 100%, var(--_shade)) 0 0 / var(--_RTL) var(--_RTL) no-repeat,
|
||||
conic-gradient(from var(--_L) at 0 100%, var(--_shade)) 100% 0 / var(--_RTR) var(--_RTR) no-repeat,
|
||||
conic-gradient(from var(--_L) at 0 0, var(--_shade)) 100% 100% / var(--_RBR) var(--_RBR) no-repeat,
|
||||
conic-gradient(from var(--_L) at 100% 0, var(--_shade)) 0 100% / var(--_RBL) var(--_RBL) no-repeat,
|
||||
linear-gradient(var(--_top) 0 0) var(--_RTL) 0 /
|
||||
calc(100% - var(--_RTL) - var(--_RTR)) var(--_top-w) no-repeat,
|
||||
linear-gradient(var(--_bottom) 0 0) var(--_RBL) 100% /
|
||||
calc(100% - var(--_RBL) - var(--_RBR)) var(--_bottom-w) no-repeat,
|
||||
linear-gradient(var(--_left) 0 0) 0 var(--_RTL) / var(--_left-w)
|
||||
calc(100% - var(--_RTL) - var(--_RBL)) no-repeat,
|
||||
linear-gradient(var(--_right) 0 0) 100% var(--_RTR) / var(--_right-w)
|
||||
calc(100% - var(--_RTR) - var(--_RBR)) no-repeat;
|
||||
background-origin: border-box;
|
||||
background-clip: border-box;
|
||||
box-shadow: none;
|
||||
-webkit-mask:
|
||||
linear-gradient(#000 0 0) padding-box,
|
||||
linear-gradient(#000 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask:
|
||||
linear-gradient(#000 0 0) padding-box,
|
||||
linear-gradient(#000 0 0);
|
||||
mask-composite: exclude;
|
||||
}
|
||||
|
||||
.bevel[data-depth='inset']::before,
|
||||
.bevel[data-profile='edge'][data-pressed='true']::before,
|
||||
.bevel[data-profile='edge'][data-depth='raised']:has(
|
||||
> :global(:is(button, [role='button']):active)
|
||||
)::before,
|
||||
.bevel[data-profile='edge'][data-depth='raised']:has(
|
||||
> :global([aria-pressed='true'])
|
||||
)::before {
|
||||
--_L: calc(var(--bevel-light) + 180deg);
|
||||
}
|
||||
|
||||
.bevel[data-profile='ridge']::before {
|
||||
--_W: var(--bevel-face-width);
|
||||
}
|
||||
|
||||
.bevel[data-profile='ridge']::after {
|
||||
--_W: var(--bevel-face-width);
|
||||
--_RTL: max(0px, calc(var(--bevel-top-left-radius) - var(--bevel-face-width)));
|
||||
--_RTR: max(0px, calc(var(--bevel-top-right-radius) - var(--bevel-face-width)));
|
||||
--_RBR: max(0px, calc(var(--bevel-bottom-right-radius) - var(--bevel-face-width)));
|
||||
--_RBL: max(0px, calc(var(--bevel-bottom-left-radius) - var(--bevel-face-width)));
|
||||
inset:
|
||||
var(--bevel-top-inset)
|
||||
var(--bevel-right-inset)
|
||||
var(--bevel-bottom-inset)
|
||||
var(--bevel-left-inset);
|
||||
}
|
||||
|
||||
.bevel[data-profile='ridge'][data-depth='raised']::after {
|
||||
--_L: calc(var(--bevel-light) + 180deg);
|
||||
}
|
||||
|
||||
.bevel[data-profile='ridge'][data-depth='inset']::after {
|
||||
--_L: var(--bevel-light);
|
||||
}
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.bevel {
|
||||
forced-color-adjust: auto;
|
||||
}
|
||||
|
||||
.bevel::before,
|
||||
.bevel[data-profile='ridge']::after {
|
||||
border-color: ButtonText;
|
||||
background: none;
|
||||
box-shadow: none;
|
||||
-webkit-mask: none;
|
||||
mask: none;
|
||||
forced-color-adjust: auto;
|
||||
}
|
||||
|
||||
.bevel[data-invalid='true'],
|
||||
.bevel:has(> :global([aria-invalid='true'])) {
|
||||
outline: 2px solid Mark;
|
||||
outline-offset: 1px;
|
||||
box-shadow: none;
|
||||
border-color: Mark;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.bevel::before,
|
||||
.bevel[data-profile='ridge']::after {
|
||||
.bevel {
|
||||
border-color: currentColor;
|
||||
background: none;
|
||||
-webkit-mask: none;
|
||||
mask: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<script lang="ts">
|
||||
export type BevelLineDirection = 'x' | 'y';
|
||||
export type BevelLineDepth = 'raised' | 'inset';
|
||||
|
||||
type Props = {
|
||||
direction?: BevelLineDirection;
|
||||
length?: string;
|
||||
depth?: BevelLineDepth;
|
||||
decorative?: boolean;
|
||||
as?: 'span' | 'div';
|
||||
class?: string;
|
||||
@@ -14,7 +12,6 @@
|
||||
let {
|
||||
direction = 'x',
|
||||
length = '100%',
|
||||
depth = 'raised',
|
||||
decorative = false,
|
||||
as = 'span',
|
||||
class: className = '',
|
||||
@@ -25,7 +22,6 @@
|
||||
this={as}
|
||||
class={`bevel-line ${className}`.trim()}
|
||||
data-direction={direction}
|
||||
data-depth={depth}
|
||||
role={decorative ? undefined : 'separator'}
|
||||
aria-orientation={decorative ? undefined : direction === 'x' ? 'horizontal' : 'vertical'}
|
||||
aria-hidden={decorative ? 'true' : undefined}
|
||||
@@ -35,112 +31,34 @@
|
||||
|
||||
<style>
|
||||
.bevel-line {
|
||||
--bevel-light: 315deg;
|
||||
--bevel-line-face-width: var(--bevel-face-width);
|
||||
--bevel-line-width: calc(var(--bevel-line-face-width) * 2);
|
||||
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden;
|
||||
border-radius: 999px;
|
||||
background: var(--line-strong);
|
||||
border-color: var(--line-strong);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.bevel-line[data-direction='x'] {
|
||||
height: var(--bevel-line-width);
|
||||
height: 0;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.bevel-line[data-direction='y'] {
|
||||
width: var(--bevel-line-width);
|
||||
}
|
||||
|
||||
@supports
|
||||
(color: color-mix(in oklab, white 50%, black)) and
|
||||
(width: calc(cos(0deg) * 1px)) {
|
||||
.bevel-line {
|
||||
--_L: var(--bevel-light);
|
||||
--_s0: var(--bevel-highlight);
|
||||
--_s1: color-mix(in oklab, var(--bevel-highlight) 96.5926%, var(--bevel-shadow));
|
||||
--_s2: color-mix(in oklab, var(--bevel-highlight) 86.6025%, var(--bevel-shadow));
|
||||
--_s3: color-mix(in oklab, var(--bevel-highlight) 70.7107%, var(--bevel-shadow));
|
||||
--_s4: color-mix(in oklab, var(--bevel-highlight) 50%, var(--bevel-shadow));
|
||||
--_s5: color-mix(in oklab, var(--bevel-highlight) 25.8819%, var(--bevel-shadow));
|
||||
--_s6: var(--bevel-shadow);
|
||||
--_shade:
|
||||
var(--_s0) 0deg,
|
||||
var(--_s1) 15deg,
|
||||
var(--_s2) 30deg,
|
||||
var(--_s3) 45deg,
|
||||
var(--_s4) 60deg,
|
||||
var(--_s5) 75deg,
|
||||
var(--_s6) 90deg,
|
||||
var(--_s6) 270deg,
|
||||
var(--_s5) 285deg,
|
||||
var(--_s4) 300deg,
|
||||
var(--_s3) 315deg,
|
||||
var(--_s2) 330deg,
|
||||
var(--_s1) 345deg,
|
||||
var(--_s0) 360deg;
|
||||
--_top: color-mix(
|
||||
in oklab,
|
||||
var(--bevel-highlight) calc(max(cos(0deg - var(--_L)), 0) * 100%),
|
||||
var(--bevel-shadow)
|
||||
);
|
||||
--_right: color-mix(
|
||||
in oklab,
|
||||
var(--bevel-highlight) calc(max(cos(90deg - var(--_L)), 0) * 100%),
|
||||
var(--bevel-shadow)
|
||||
);
|
||||
--_bottom: color-mix(
|
||||
in oklab,
|
||||
var(--bevel-highlight) calc(max(cos(180deg - var(--_L)), 0) * 100%),
|
||||
var(--bevel-shadow)
|
||||
);
|
||||
--_left: color-mix(
|
||||
in oklab,
|
||||
var(--bevel-highlight) calc(max(cos(270deg - var(--_L)), 0) * 100%),
|
||||
var(--bevel-shadow)
|
||||
);
|
||||
}
|
||||
|
||||
.bevel-line[data-depth='inset'] {
|
||||
--_L: calc(var(--bevel-light) + 180deg);
|
||||
}
|
||||
|
||||
.bevel-line[data-direction='x'] {
|
||||
background:
|
||||
conic-gradient(from var(--_L) at 100% 50%, var(--_shade)) 0 0 /
|
||||
var(--bevel-line-face-width) var(--bevel-line-width) no-repeat,
|
||||
conic-gradient(from var(--_L) at 0 50%, var(--_shade)) 100% 0 /
|
||||
var(--bevel-line-face-width) var(--bevel-line-width) no-repeat,
|
||||
linear-gradient(to bottom, var(--_top) 0 50%, var(--_bottom) 50% 100%)
|
||||
var(--bevel-line-face-width) 0 /
|
||||
calc(100% - var(--bevel-line-width)) 100% no-repeat;
|
||||
}
|
||||
|
||||
.bevel-line[data-direction='y'] {
|
||||
background:
|
||||
conic-gradient(from var(--_L) at 50% 100%, var(--_shade)) 0 0 /
|
||||
var(--bevel-line-width) var(--bevel-line-face-width) no-repeat,
|
||||
conic-gradient(from var(--_L) at 50% 0, var(--_shade)) 0 100% /
|
||||
var(--bevel-line-width) var(--bevel-line-face-width) no-repeat,
|
||||
linear-gradient(to right, var(--_left) 0 50%, var(--_right) 50% 100%)
|
||||
0 var(--bevel-line-face-width) / 100%
|
||||
calc(100% - var(--bevel-line-width)) no-repeat;
|
||||
}
|
||||
width: 0;
|
||||
border-left-style: solid;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
@media (forced-colors: active) {
|
||||
.bevel-line {
|
||||
background: CanvasText;
|
||||
border-color: CanvasText;
|
||||
forced-color-adjust: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.bevel-line {
|
||||
background: currentColor;
|
||||
border-color: currentColor;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
workspaceError={data.workspaceCatalogError}
|
||||
/>
|
||||
</SidebarFrame>
|
||||
<Bevel as="div" class="app-shell__topbar-bevel" depth="inset" top={false} right={false} left={false} fill>
|
||||
<Bevel as="div" class="app-shell__topbar-bevel" top={false} right={false} left={false} fill>
|
||||
<header class="app-shell__topbar">
|
||||
<div class="app-shell__topbar-location">
|
||||
{#if headerController.content}{@render headerController.content()}{/if}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<h1 id="action-heading" class="showroom-heading">Action</h1>
|
||||
<BevelLine direction="x" length="100%" decorative />
|
||||
<div class="action-row">
|
||||
<Bevel profile="edge" depth="raised">
|
||||
<Bevel>
|
||||
<button class="action action--primary" type="button">Create Worker</button>
|
||||
</Bevel>
|
||||
<Tooltip id="rerun-help" text="Runs validation against the current Workspace configuration." placement="bottom" align="start">
|
||||
@@ -28,22 +28,22 @@
|
||||
</Bevel>
|
||||
</div>
|
||||
|
||||
<div class="ridge-matrix">
|
||||
<Bevel as="div" profile="ridge" depth="raised" fill>
|
||||
<div class="tactile-group" role="group" aria-label="Raised validation controls">
|
||||
<div class="border-matrix">
|
||||
<Bevel as="div" fill>
|
||||
<div class="tactile-group" role="group" aria-label="Bordered validation controls">
|
||||
<span>
|
||||
<strong>Raised ridge</strong>
|
||||
<strong>Closed border</strong>
|
||||
<small>3 checks passed at 14:32</small>
|
||||
</span>
|
||||
<Bevel pressed>
|
||||
<Bevel>
|
||||
<button class="compact-control" type="button" aria-pressed="true">Live</button>
|
||||
</Bevel>
|
||||
</div>
|
||||
</Bevel>
|
||||
<Bevel as="div" profile="ridge" depth="inset" fill>
|
||||
<div class="tactile-group" role="group" aria-label="Inset ridge controls">
|
||||
<Bevel as="div" fill>
|
||||
<div class="tactile-group" role="group" aria-label="Read-only bordered controls">
|
||||
<span>
|
||||
<strong>Inset ridge</strong>
|
||||
<strong>Read-only border</strong>
|
||||
<small>Read-only control group</small>
|
||||
</span>
|
||||
<span class="status"><span class="status__marker" data-tone="muted"></span>Locked</span>
|
||||
@@ -57,7 +57,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</Bevel>
|
||||
<Bevel as="div" profile="ridge" depth="inset" top={false} left={false} fill>
|
||||
<Bevel as="div" top={false} left={false} fill>
|
||||
<div class="tactile-group" role="group" aria-label="Right and bottom groove">
|
||||
<span>
|
||||
<strong>Right + bottom</strong>
|
||||
@@ -73,10 +73,10 @@
|
||||
<BevelLine direction="x" length="100%" decorative />
|
||||
<div class="line-showcase">
|
||||
<div class="line-sample line-sample--horizontal">
|
||||
<span class="line-sample__label">Raised · x · 100%</span>
|
||||
<span class="line-sample__label">Horizontal · 100%</span>
|
||||
<BevelLine direction="x" length="100%" />
|
||||
<span class="line-sample__label">Inset · x · 160px</span>
|
||||
<BevelLine direction="x" length="160px" depth="inset" />
|
||||
<span class="line-sample__label">Horizontal · 160px</span>
|
||||
<BevelLine direction="x" length="160px" />
|
||||
</div>
|
||||
<div class="line-sample line-sample--vertical">
|
||||
<span>Before</span>
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
<div class="form-grid">
|
||||
<label class="form-field">
|
||||
<span>Runtime label</span>
|
||||
<Bevel profile="edge" depth="inset" fill>
|
||||
<Bevel fill>
|
||||
<input name="runtime-label" value="Remote Linux runner" />
|
||||
</Bevel>
|
||||
</label>
|
||||
|
||||
<label class="form-field">
|
||||
<span>Endpoint</span>
|
||||
<Bevel profile="edge" depth="inset" fill invalid>
|
||||
<Bevel fill invalid>
|
||||
<input
|
||||
name="runtime-endpoint"
|
||||
value="http://runner.internal.example:8787/api/runtime"
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
<label class="form-field">
|
||||
<span>Architecture</span>
|
||||
<Bevel profile="edge" depth="inset" fill>
|
||||
<Bevel fill>
|
||||
<select name="architecture">
|
||||
<option>arm64</option>
|
||||
<option selected>x86_64</option>
|
||||
|
||||
@@ -153,7 +153,7 @@
|
||||
font: 600 12px/1 var(--font-mono);
|
||||
}
|
||||
|
||||
.ridge-matrix {
|
||||
.border-matrix {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: var(--space-4);
|
||||
@@ -637,7 +637,7 @@
|
||||
|
||||
.showroom-split,
|
||||
.line-showcase,
|
||||
.ridge-matrix {
|
||||
.border-matrix {
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-6);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user