Squirclesssss 🟦🔵 (#22535)

This commit is contained in:
Thomas des Francs
2026-07-04 07:07:29 +02:00
committed by GitHub
parent badaae17cd
commit e609320666
130 changed files with 517 additions and 149 deletions
@@ -4,7 +4,7 @@
flex-shrink: 0;
overflow: hidden;
user-select: none;
border-radius: 2px;
border-radius: var(--t-border-radius-xs);
border: var(--avatar-border, none);
box-sizing: border-box;
display: flex;
@@ -13,11 +13,12 @@
background: var(--avatar-background);
&[data-type='icon'] {
border-radius: 4px;
border-radius: var(--t-border-radius-sm);
}
&[data-type='rounded'] {
border-radius: 50%;
corner-shape: round;
}
&[data-clickable] {
@@ -4,7 +4,7 @@
justify-content: center;
width: 14px;
height: 14px;
border-radius: 4px;
border-radius: var(--t-border-radius-sm);
background-color: var(
--avatar-or-icon-background,
var(--t-background-inverted-secondary)
@@ -5,5 +5,6 @@
width: var(--t-spacing-5);
height: var(--t-spacing-5);
border-radius: var(--t-border-radius-rounded);
corner-shape: round;
background-color: var(--t-color-blue);
}
@@ -17,7 +17,8 @@
font-weight: inherit;
background-color: inherit;
border: none;
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-sm-round);
corner-shape: round;
& > svg {
flex-shrink: 0;
@@ -5,13 +5,15 @@
width: var(--t-spacing-3);
height: var(--t-spacing-4);
border: 1px solid var(--color-sample-border-color);
border-radius: 60px;
border-radius: var(--t-border-radius-pill);
corner-shape: round;
background-color: var(--color-sample-color);
}
.circle {
width: var(--t-spacing-4);
border-radius: 50%;
corner-shape: round;
}
.pipeline {
@@ -25,6 +27,7 @@
width: var(--t-spacing-1);
height: var(--t-spacing-1);
border-radius: var(--t-border-radius-rounded);
corner-shape: round;
background-color: var(--color-sample-color);
content: '';
}
@@ -2,6 +2,7 @@
width: 14px;
height: 14px;
border-radius: 50%;
corner-shape: round;
display: flex;
align-items: center;
justify-content: center;
@@ -2,6 +2,7 @@
align-items: center;
background: var(--t-background-transparent-light);
border-radius: var(--t-border-radius-pill);
corner-shape: round;
color: var(--t-font-color-light);
display: inline-flex;
font-size: var(--t-font-size-xs);
@@ -2,6 +2,7 @@
align-items: center;
background: var(--status-background);
border-radius: var(--t-border-radius-pill);
corner-shape: round;
color: var(--status-text-color);
display: inline-flex;
font-size: var(--t-font-size-md);
@@ -19,6 +20,7 @@
&:before {
background-color: var(--status-text-color);
border-radius: var(--t-border-radius-rounded);
corner-shape: round;
content: '';
display: block;
flex-shrink: 0;
@@ -7,6 +7,6 @@
width: var(--tinted-icon-tile-dimension, var(--t-spacing-4));
height: var(--tinted-icon-tile-dimension, var(--t-spacing-4));
border: var(--tinted-icon-tile-border, none);
border-radius: 4px;
border-radius: var(--t-border-radius-sm);
background-color: var(--tinted-icon-tile-background-color, transparent);
}
@@ -2,7 +2,8 @@
box-sizing: content-box;
align-items: center;
background: var(--tag-background);
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-sm-round);
corner-shape: round;
color: var(--tag-text);
display: inline-flex;
font-size: var(--t-font-size-md);
@@ -7,6 +7,7 @@
width: var(--t-spacing-6);
height: var(--t-spacing-3);
border-radius: var(--t-border-radius-pill);
corner-shape: round;
border: 1px solid
var(--loader-color, var(--tw-button-color, var(--t-font-color-tertiary)));
overflow: hidden;
@@ -18,6 +19,7 @@
var(--tw-button-color, var(--t-font-color-tertiary))
);
border-radius: var(--t-border-radius-pill);
corner-shape: round;
height: 8px;
width: 8px;
animation: loaderDot 0.8s ease-in-out infinite;
@@ -1,7 +1,7 @@
.root {
background-color: var(--t-background-primary);
border: 0.75px solid var(--t-border-color-medium);
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-md);
display: flex;
justify-content: center;
}
@@ -6,7 +6,7 @@
align-items: center;
background: var(--abtn-bg);
border-color: var(--abtn-border-color);
border-radius: var(--abtn-radius, var(--t-border-radius-sm));
border-radius: var(--abtn-radius, var(--t-border-radius-md));
border-style: solid;
border-width: var(--abtn-border-width);
box-shadow: var(--abtn-box-shadow);
@@ -48,7 +48,7 @@
// These rules only assign the custom property so the border-radius
// declaration stays on the flat .button class for overridability.
.button[data-position='left'] {
--abtn-radius: var(--t-border-radius-sm) 0px 0px var(--t-border-radius-sm);
--abtn-radius: var(--t-border-radius-md) 0px 0px var(--t-border-radius-md);
}
.button[data-position='middle'] {
@@ -56,7 +56,7 @@
}
.button[data-position='right'] {
--abtn-radius: 0px var(--t-border-radius-sm) var(--t-border-radius-sm) 0px;
--abtn-radius: 0px var(--t-border-radius-md) var(--t-border-radius-md) 0px;
}
// --abtn-square-size keeps the square width declaration flat on .square.
@@ -5,7 +5,7 @@
align-items: center;
background: transparent;
border: none;
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-md);
box-shadow: none;
color: var(--t-font-color-secondary);
cursor: pointer;
@@ -30,7 +30,7 @@ $gray-scale-light-gray1: color(display-p3 1 1 1);
align-items: center;
background: var(--btn-bg, transparent);
border-color: var(--btn-border-color, transparent);
border-radius: var(--btn-radius, var(--t-border-radius-sm));
border-radius: var(--btn-radius, var(--t-border-radius-md));
border-style: solid;
border-width: var(--btn-border-width, 1px 1px 1px 1px);
box-shadow: var(--btn-box-shadow, none);
@@ -87,7 +87,7 @@ $gray-scale-light-gray1: color(display-p3 1 1 1);
// '1px 1px' override for primary/danger is omitted: it computes to the same
// 1px on every side as the default.
.button[data-position='left'] {
--btn-radius: var(--t-border-radius-sm) 0px 0px var(--t-border-radius-sm);
--btn-radius: var(--t-border-radius-md) 0px 0px var(--t-border-radius-md);
--btn-border-width: 1px 0px 1px 1px;
}
@@ -97,7 +97,7 @@ $gray-scale-light-gray1: color(display-p3 1 1 1);
}
.button[data-position='right'] {
--btn-radius: 0px var(--t-border-radius-sm) var(--t-border-radius-sm) 0px;
--btn-radius: 0px var(--t-border-radius-md) var(--t-border-radius-md) 0px;
--btn-border-width: 1px 1px 1px 0px;
}
@@ -107,8 +107,8 @@ $gray-scale-light-gray1: color(display-p3 1 1 1);
// danger keep the focus border even when disabled.
$primary-accents: (
'default': (
bg: var(--t-background-secondary),
bg-disabled: var(--t-background-secondary),
bg: var(--t-background-transparent-lighter),
bg-disabled: var(--t-background-transparent-lighter),
border: var(--t-background-transparent-light),
border-focus: var(--t-color-blue),
focus-needs-enabled: true,
@@ -4,10 +4,11 @@
--checkbox-border-width: 1px;
--checkbox-bg: transparent;
--checkbox-border-color: var(--t-border-color-secondary-inverted);
--checkbox-box-radius: var(--t-border-radius-sm);
--checkbox-box-radius: var(--t-border-radius-sm-round);
align-items: center;
border-radius: var(--t-border-radius-md);
border-radius: var(--t-border-radius-md-round);
corner-shape: round;
cursor: pointer;
display: flex;
padding: 0;
@@ -39,15 +40,17 @@
}
.squared {
--checkbox-box-radius: var(--t-border-radius-sm);
--checkbox-box-radius: var(--t-border-radius-sm-round);
border-radius: var(--t-border-radius-md);
border-radius: var(--t-border-radius-md-round);
}
.rounded {
--checkbox-box-radius: var(--t-border-radius-rounded);
border-radius: var(--t-border-radius-rounded);
corner-shape: round;
}
.blue {
@@ -114,6 +117,7 @@
background: var(--checkbox-bg);
border-color: var(--checkbox-border-color);
border-radius: var(--checkbox-box-radius);
corner-shape: round;
border-style: solid;
border-width: var(--checkbox-border-width);
// The border sits outside the declared box size, so size against the content box.
@@ -9,13 +9,13 @@
&[data-variant='default'] {
border: 1px solid var(--t-border-color-medium);
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-md);
}
&[data-variant='with-header'] {
border: 1px solid var(--t-border-color-medium);
border-top: none;
border-radius: 0 0 var(--t-border-radius-sm) var(--t-border-radius-sm);
border-radius: 0 0 var(--t-border-radius-md) var(--t-border-radius-md);
}
}
@@ -29,7 +29,7 @@
.editorWrapper :global(.monaco-editor) {
outline-width: 0;
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-md);
background-color: var(--t-background-secondary);
}
@@ -49,11 +49,11 @@
.editorWrapper[data-variant='default'] :global(.overflow-guard) {
border: 1px solid var(--t-border-color-medium);
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-md);
}
.editorWrapper[data-variant='with-header'] :global(.overflow-guard) {
border: 1px solid var(--t-border-color-medium);
border-top: none;
border-radius: 0 0 var(--t-border-radius-sm) var(--t-border-radius-sm);
border-radius: 0 0 var(--t-border-radius-md) var(--t-border-radius-md);
}
@@ -5,8 +5,8 @@
height: var(--t-spacing-10);
padding: 0 var(--t-spacing-2);
border: 1px solid var(--t-border-color-medium);
border-top-left-radius: var(--t-border-radius-sm);
border-top-right-radius: var(--t-border-radius-sm);
border-top-left-radius: var(--t-border-radius-md);
border-top-right-radius: var(--t-border-radius-md);
background-color: var(--t-background-transparent-lighter);
color: var(--t-font-color-tertiary);
font-weight: var(--t-font-weight-medium);
@@ -8,7 +8,7 @@
backdrop-filter: var(--floating-button-backdrop-filter, none);
background: var(--t-background-primary);
border: var(--floating-button-border, none);
border-radius: var(--floating-button-radius, var(--t-border-radius-sm));
border-radius: var(--floating-button-radius, var(--t-border-radius-md));
box-shadow: var(--floating-button-box-shadow, none);
color: var(--floating-button-color, var(--t-font-color-secondary));
cursor: pointer;
@@ -57,8 +57,8 @@
// position only reshapes the radius; standalone keeps the .button default.
.button[data-position='left'] {
--floating-button-radius: var(--t-border-radius-sm) 0px 0px
var(--t-border-radius-sm);
--floating-button-radius: var(--t-border-radius-md) 0px 0px
var(--t-border-radius-md);
}
.button[data-position='middle'] {
@@ -66,8 +66,8 @@
}
.button[data-position='right'] {
--floating-button-radius: 0px var(--t-border-radius-sm)
var(--t-border-radius-sm) 0px;
--floating-button-radius: 0px var(--t-border-radius-md)
var(--t-border-radius-md) 0px;
}
.button[data-apply-blur] {
@@ -9,7 +9,7 @@
backdrop-filter: none;
background: var(--t-background-primary);
border: 1px solid var(--t-border-color-strong);
border-radius: var(--floating-icon-button-radius, var(--t-border-radius-sm));
border-radius: var(--floating-icon-button-radius, var(--t-border-radius-md));
box-shadow: none;
box-sizing: border-box;
color: var(--t-font-color-tertiary);
@@ -47,9 +47,13 @@
width: 28px;
}
// Grouped edges stay concentric with the FloatingIconButtonGroup container:
// its sm radius minus the 2px padding. The group container uses sm (not md)
// because at these sizes (24-32px) md would exceed 50% of the box, clamp to a
// full squircle, and break concentricity — see the group stylesheet.
.button[data-position='left'] {
--floating-icon-button-radius: var(--t-border-radius-sm) 0px 0px
var(--t-border-radius-sm);
--floating-icon-button-radius: calc(var(--t-border-radius-sm) - 2px) 0px 0px
calc(var(--t-border-radius-sm) - 2px);
}
.button[data-position='middle'] {
@@ -57,8 +61,8 @@
}
.button[data-position='right'] {
--floating-icon-button-radius: 0px var(--t-border-radius-sm)
var(--t-border-radius-sm) 0px;
--floating-icon-button-radius: 0px calc(var(--t-border-radius-sm) - 2px)
calc(var(--t-border-radius-sm) - 2px) 0px;
}
.button[data-apply-blur] {
@@ -1,6 +1,10 @@
.container {
backdrop-filter: blur(20px);
background-color: var(--t-background-primary);
// sm, not md: the container is only 24-32px tall, where md would exceed 50%
// of the box and clamp to a full squircle. A scaled full squircle is not a
// uniform offset, so the child buttons' corners would pull away from the
// frame. sm stays below the clamp point, keeping the group concentric.
border-radius: var(--t-border-radius-sm);
box-shadow:
0px 2px 4px 0px var(--t-background-transparent-light),
@@ -14,7 +14,7 @@
align-items: center;
background: var(--icon-button-bg, transparent);
border-color: var(--icon-button-border-color, transparent);
border-radius: var(--icon-button-radius, var(--t-border-radius-sm));
border-radius: var(--icon-button-radius, var(--t-border-radius-md));
border-style: solid;
border-width: var(--icon-button-border-width, 1px);
box-shadow: var(--icon-button-box-shadow, none);
@@ -62,10 +62,11 @@
}
// position only reshapes the border radius; standalone keeps the .button
// fallback radius.
// fallback radius. Grouped IconButtons abut flush (no framing container), so
// their outer corners share the standalone md radius.
.button[data-position='left'] {
--icon-button-radius: var(--t-border-radius-sm) 0px 0px
var(--t-border-radius-sm);
--icon-button-radius: var(--t-border-radius-md) 0px 0px
var(--t-border-radius-md);
}
.button[data-position='middle'] {
@@ -73,7 +74,7 @@
}
.button[data-position='right'] {
--icon-button-radius: 0px var(--t-border-radius-sm) var(--t-border-radius-sm)
--icon-button-radius: 0px var(--t-border-radius-md) var(--t-border-radius-md)
0px;
}
@@ -102,7 +103,7 @@
// keeps the base/focus value, mirroring the legacy switch.
$primary-accents: (
'default': (
bg: var(--t-background-secondary),
bg: var(--t-background-transparent-lighter),
border: var(--t-background-transparent-light),
border-focus: var(--t-color-blue),
border-disabled: null,
@@ -112,8 +113,8 @@ $primary-accents: (
opacity-disabled: null,
hover: var(--t-background-tertiary),
active: var(--t-background-quaternary),
hover-disabled: var(--t-background-secondary),
active-disabled: var(--t-background-secondary),
hover-disabled: var(--t-background-transparent-lighter),
active-disabled: var(--t-background-transparent-lighter),
),
'blue': (
bg: var(--t-color-blue),
@@ -3,6 +3,9 @@
backdrop-filter: blur(20px);
background-color: var(--t-background-transparent-lighter);
border: 1px solid var(--t-border-color-strong);
// sm, not md: the container is ~26px tall, where md would exceed 50% of the
// box and clamp to a full squircle, breaking concentricity with the inner
// buttons (see FloatingIconButtonGroup for the full rationale).
border-radius: var(--t-border-radius-sm);
display: inline-flex;
gap: 2px;
@@ -2,7 +2,9 @@
align-items: center;
border: none;
background-color: transparent;
border-radius: var(--t-border-radius-xs);
// Concentric with the IconButtonGroup sm container: its radius minus the
// 2px padding and 1px border.
border-radius: calc(var(--t-border-radius-sm) - 3px);
color: var(--t-font-color-tertiary);
cursor: pointer;
display: flex;
@@ -9,7 +9,7 @@
align-items: center;
background: transparent;
border: var(--light-button-border, none);
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-md);
box-shadow: var(--light-button-box-shadow, none);
color: var(--light-button-color);
cursor: pointer;
@@ -9,7 +9,9 @@
align-items: center;
background: transparent;
border: var(--light-icon-button-border, none);
border-radius: var(--t-border-radius-sm);
// Containers that frame the button (e.g. RecordTableCellButtons) publish
// this variable to keep the hover shape concentric with their own radius.
border-radius: var(--light-icon-button-radius, var(--t-border-radius-md));
box-shadow: var(--light-icon-button-box-shadow, none);
color: var(--light-icon-button-color);
cursor: pointer;
@@ -12,6 +12,7 @@
background-color: transparent;
border: 1px solid var(--t-font-color-secondary);
border-radius: var(--t-border-radius-rounded);
corner-shape: round;
// The border sits outside the declared size, matching the legacy input rendering.
box-sizing: content-box;
display: block;
@@ -50,6 +51,7 @@
.radio[data-checked]::after {
background-color: var(--t-gray-scale-gray1);
border-radius: 50%;
corner-shape: round;
content: '';
height: 6px;
left: 50%;
@@ -3,6 +3,7 @@
background: var(--t-color-blue);
border: none;
border-radius: 50%;
corner-shape: round;
color: var(--t-font-color-inverted);
cursor: pointer;
display: flex;
@@ -9,7 +9,7 @@
align-items: center;
background-color: var(--t-background-transparent-lighter);
border: 1px solid var(--t-border-color-medium);
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-md);
box-sizing: border-box;
display: flex;
flex: 1;
@@ -3,6 +3,7 @@
background: var(--t-background-secondary);
border: 1px solid var(--t-border-color-medium);
border-radius: var(--t-border-radius-pill);
corner-shape: round;
box-sizing: border-box;
display: inline-flex;
flex-shrink: 0;
@@ -17,6 +18,7 @@
background: transparent;
border: 0;
border-radius: var(--t-border-radius-pill);
corner-shape: round;
color: var(--t-font-color-secondary);
cursor: pointer;
display: flex;
@@ -20,6 +20,7 @@
.track {
background: var(--t-background-tertiary);
border-radius: var(--t-border-radius-pill);
corner-shape: round;
height: var(--slider-track-size);
inset: 0;
overflow: hidden;
@@ -29,6 +30,7 @@
.fill {
background: var(--slider-main-color);
border-radius: var(--t-border-radius-pill);
corner-shape: round;
height: 100%;
left: 0;
min-width: var(--slider-track-size);
@@ -39,6 +41,7 @@
&::after {
background: var(--t-background-primary);
border-radius: var(--t-border-radius-rounded);
corner-shape: round;
content: '';
height: var(--slider-thumb-size);
position: absolute;
@@ -70,6 +73,7 @@
background: transparent;
border: 0;
border-radius: var(--t-border-radius-rounded);
corner-shape: round;
height: var(--slider-thumb-size);
margin-top: 2px;
width: var(--slider-thumb-size);
@@ -85,6 +89,7 @@
background: transparent;
border: 0;
border-radius: var(--t-border-radius-rounded);
corner-shape: round;
height: var(--slider-thumb-size);
width: var(--slider-thumb-size);
}
@@ -84,7 +84,7 @@
font-weight: var(--t-font-weight-medium);
width: 100%;
white-space: nowrap;
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-md);
&[data-content-size='sm'] {
padding: var(--t-spacing-1) var(--t-spacing-2);
@@ -4,7 +4,8 @@
flex-shrink: 0;
align-items: center;
align-self: flex-start;
border-radius: 10px;
border-radius: var(--t-border-radius-pill);
corner-shape: round;
background-color: var(--t-background-transparent-medium);
cursor: pointer;
transition: background-color duration(normal) ease;
@@ -39,6 +40,7 @@
left: 0;
display: block;
border-radius: 50%;
corner-shape: round;
background-color: var(--t-background-primary);
transition: transform duration(normal) ease;
}
@@ -10,6 +10,7 @@
.bar {
background-color: var(--t-background-quaternary);
border-radius: var(--t-border-radius-pill);
corner-shape: round;
height: 3px;
transition: background-color duration(fast);
width: 32px;
@@ -7,7 +7,8 @@
// Height and width below are sized against the content box.
box-sizing: content-box;
align-items: center;
border-radius: var(--t-border-radius-sm);
// Concentric with the md dropdown container: parent radius minus its inset.
border-radius: calc(var(--t-border-radius-md) - var(--t-spacing-1));
display: flex;
flex-direction: row;
font-size: var(--t-font-size-sm);
@@ -17,7 +17,8 @@
align-items: center;
background-color: var(--t-background-secondary);
border: 1px solid var(--t-border-color-strong);
border-radius: var(--t-border-radius-sm);
// Concentric with the md dropdown container: parent radius minus its inset.
border-radius: calc(var(--t-border-radius-md) - var(--t-spacing-1));
box-shadow: var(--t-box-shadow-underline);
display: flex;
flex-direction: column;
@@ -5,7 +5,8 @@
// Height and width below are sized against the content box.
box-sizing: content-box;
align-items: center;
border-radius: var(--t-border-radius-sm);
// Concentric with the md dropdown container: parent radius minus its inset.
border-radius: calc(var(--t-border-radius-md) - var(--t-spacing-1));
display: flex;
flex-direction: row;
font-size: var(--t-font-size-sm);
@@ -5,7 +5,8 @@
// Height below is sized against the content box.
box-sizing: content-box;
align-items: center;
border-radius: var(--t-border-radius-sm);
// Concentric with the md dropdown container: parent radius minus its inset.
border-radius: calc(var(--t-border-radius-md) - var(--t-spacing-1));
cursor: pointer;
display: flex;
flex-direction: row;
@@ -5,7 +5,8 @@
outline: inherit;
align-items: center;
border: 1px solid var(--t-border-color-medium);
border-radius: var(--t-border-radius-sm);
// Concentric with the md dropdown container: parent radius minus its inset.
border-radius: calc(var(--t-border-radius-md) - var(--t-spacing-1));
color: var(--t-font-color-tertiary);
cursor: pointer;
display: flex;
@@ -2,7 +2,8 @@
align-items: center;
background-color: var(--t-background-transparent-lighter);
border: 1px solid var(--t-border-color-strong);
border-radius: 50px;
border-radius: var(--t-border-radius-pill);
corner-shape: round;
// Height and max-width are sized against the content box.
box-sizing: content-box;
color: var(--t-font-color-primary);
@@ -8,7 +8,7 @@
.tooltip {
backdrop-filter: var(--t-blur-strong);
background-color: var(--t-color-transparent-gray11);
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-md);
box-shadow: var(--t-box-shadow-light);
color: var(--t-gray-scale-gray1);
font-size: var(--t-font-size-sm);
@@ -1,7 +1,7 @@
.card {
background-color: var(--card-background-color, transparent);
border: 1px solid var(--t-border-color-medium);
border-radius: var(--t-border-radius-sm);
border-radius: var(--t-border-radius-md);
color: var(--t-font-color-secondary);
overflow: hidden;
width: auto;
@@ -0,0 +1,102 @@
import * as fs from 'fs';
import * as path from 'path';
import { BORDER_COMMON } from '@ui/theme/constants/BorderCommon';
// The squircle block in the theme CSS doubles every corner radius token,
// because a squircle needs ~2x the radius of a round corner to read as the
// same visual size. These tests pin that invariant so the base scale and the
// squircle overrides cannot drift apart, and keep the committed dist copies
// in sync with the source files.
const THEME_CONSTANTS_DIR = path.resolve(__dirname, '..');
const DIST_DIR = path.resolve(__dirname, '../../../dist');
const CORNER_RADIUS_TOKENS = ['xs', 'sm', 'md', 'lg', 'xl', 'xxl'] as const;
const MAXED_RADIUS_TOKENS = ['pill', 'rounded'] as const;
// Shape-independent radii for elements that keep corner-shape: round
// (checkboxes, chips, tags): same visual size in both modes.
const ROUND_RADIUS_TOKENS = [
{ token: 'sm-round', matchesBaseToken: 'sm' },
{ token: 'md-round', matchesBaseToken: 'md' },
] as const;
const readThemeCss = (fileName: string) =>
fs.readFileSync(path.join(THEME_CONSTANTS_DIR, fileName), 'utf-8');
const extractSquircleBlock = (css: string) => {
const match = css.match(
/@supports \(corner-shape: squircle\) \{([\s\S]*?)\n\}/,
);
if (match === null) {
throw new Error('Missing @supports (corner-shape: squircle) block');
}
return match[1];
};
const extractRadiusPx = (css: string, token: string) => {
const match = css.match(new RegExp(`--t-border-radius-${token}: (\\d+)px;`));
if (match === null) {
throw new Error(`Missing --t-border-radius-${token} declaration`);
}
return Number(match[1]);
};
describe.each(['theme-light.css', 'theme-dark.css'])(
'squircle corner overrides in %s',
(fileName) => {
const css = readThemeCss(fileName);
const squircleBlock = extractSquircleBlock(css);
const baseCss = css.replace(squircleBlock, '');
it.each(CORNER_RADIUS_TOKENS)(
'doubles the %s radius token for squircle corners',
(token) => {
const baseValue = extractRadiusPx(baseCss, token);
const squircleValue = extractRadiusPx(squircleBlock, token);
expect(squircleValue).toBe(baseValue * 2);
},
);
it.each(CORNER_RADIUS_TOKENS)(
'keeps the base %s radius token aligned with BORDER_COMMON',
(token) => {
expect(`${extractRadiusPx(baseCss, token)}px`).toBe(
BORDER_COMMON.radius[token],
);
},
);
it.each(MAXED_RADIUS_TOKENS)(
'does not redefine the %s token, which is already at its geometric maximum',
(token) => {
expect(squircleBlock).not.toContain(`--t-border-radius-${token}`);
},
);
it.each(ROUND_RADIUS_TOKENS)(
'keeps the shape-independent $token token undoubled and equal to the base $matchesBaseToken value',
({ token, matchesBaseToken }) => {
expect(squircleBlock).not.toContain(`--t-border-radius-${token}`);
expect(extractRadiusPx(baseCss, token)).toBe(
extractRadiusPx(baseCss, matchesBaseToken),
);
},
);
it('applies corner-shape universally with the --t-corner-shape opt-out', () => {
expect(squircleBlock).toContain(
'corner-shape: var(--t-corner-shape, squircle);',
);
});
it('is mirrored byte-for-byte into the committed dist copy', () => {
expect(fs.readFileSync(path.join(DIST_DIR, fileName), 'utf-8')).toBe(css);
});
},
);
@@ -136,10 +136,13 @@
--t-border-radius-xs: 2px;
--t-border-radius-sm: 4px;
--t-border-radius-md: 8px;
--t-border-radius-lg: 16px;
--t-border-radius-xl: 20px;
--t-border-radius-xxl: 40px;
--t-border-radius-pill: 999px;
--t-border-radius-rounded: 100%;
--t-border-radius-sm-round: 4px;
--t-border-radius-md-round: 8px;
--t-box-shadow-color: rgba(0, 0, 0, 0.6);
--t-box-shadow-light:
0px 2px 4px 0px rgba(0, 0, 0, 0.04), 0px 0px 4px 0px rgba(0, 0, 0, 0.08);
@@ -1005,3 +1008,37 @@
--t-color-transparent-amber11: #ffca16;
--t-color-transparent-amber12: #ffe7b3;
}
/* Squircle corners: progressive enhancement for browsers supporting
corner-shape (Chromium 139+). A squircle needs ~2x the radius of a round
corner to read as the same visual size, so the radius tokens are doubled
here; other browsers keep the base tokens as plain rounded corners.
Shape rules:
- pill, rounded and the *-round tokens are never doubled: they belong to
elements that keep corner-shape: round (circles, capsules, checkboxes,
chips, tags) and must look identical in both modes.
- Opt a subtree out of squircles with --t-corner-shape: round, or a
single element with corner-shape: round.
- Nested radii must stay concentric in both modes: derive them from the
parent radius token minus the inset, e.g.
calc(var(--t-border-radius-md) - var(--t-spacing-1)).
The token doubles under squircle while the inset does not, which is
exactly what concentricity requires. */
@supports (corner-shape: squircle) {
.dark {
--t-border-radius-xs: 4px;
--t-border-radius-sm: 8px;
--t-border-radius-md: 16px;
--t-border-radius-lg: 32px;
--t-border-radius-xl: 40px;
--t-border-radius-xxl: 80px;
}
/* Zero specificity on purpose: any component rule can override it. */
*,
*::before,
*::after {
corner-shape: var(--t-corner-shape, squircle);
}
}
@@ -136,10 +136,13 @@
--t-border-radius-xs: 2px;
--t-border-radius-sm: 4px;
--t-border-radius-md: 8px;
--t-border-radius-lg: 16px;
--t-border-radius-xl: 20px;
--t-border-radius-xxl: 40px;
--t-border-radius-pill: 999px;
--t-border-radius-rounded: 100%;
--t-border-radius-sm-round: 4px;
--t-border-radius-md-round: 8px;
--t-box-shadow-color: color(display-p3 0 0 0 / 0.039);
--t-box-shadow-light:
0px 2px 4px 0px color(display-p3 0 0 0 / 0.039),
@@ -1009,3 +1012,37 @@
--t-color-transparent-amber11: #ab6400;
--t-color-transparent-amber12: #341500dd;
}
/* Squircle corners: progressive enhancement for browsers supporting
corner-shape (Chromium 139+). A squircle needs ~2x the radius of a round
corner to read as the same visual size, so the radius tokens are doubled
here; other browsers keep the base tokens as plain rounded corners.
Shape rules:
- pill, rounded and the *-round tokens are never doubled: they belong to
elements that keep corner-shape: round (circles, capsules, checkboxes,
chips, tags) and must look identical in both modes.
- Opt a subtree out of squircles with --t-corner-shape: round, or a
single element with corner-shape: round.
- Nested radii must stay concentric in both modes: derive them from the
parent radius token minus the inset, e.g.
calc(var(--t-border-radius-md) - var(--t-spacing-1)).
The token doubles under squircle while the inset does not, which is
exactly what concentricity requires. */
@supports (corner-shape: squircle) {
.light {
--t-border-radius-xs: 4px;
--t-border-radius-sm: 8px;
--t-border-radius-md: 16px;
--t-border-radius-lg: 32px;
--t-border-radius-xl: 40px;
--t-border-radius-xxl: 80px;
}
/* Zero specificity on purpose: any component rule can override it. */
*,
*::before,
*::after {
corner-shape: var(--t-corner-shape, squircle);
}
}
@@ -174,6 +174,9 @@ export const themeCssVariables = {
xs: 'var(--t-border-radius-xs)',
sm: 'var(--t-border-radius-sm)',
md: 'var(--t-border-radius-md)',
smRound: 'var(--t-border-radius-sm-round)',
mdRound: 'var(--t-border-radius-md-round)',
lg: 'var(--t-border-radius-lg)',
xl: 'var(--t-border-radius-xl)',
xxl: 'var(--t-border-radius-xxl)',
pill: 'var(--t-border-radius-pill)',
@@ -3,6 +3,9 @@ export const BORDER_COMMON = {
xs: '2px',
sm: '4px',
md: '8px',
smRound: '4px',
mdRound: '8px',
lg: '16px',
xl: '20px',
xxl: '40px',
pill: '999px',