Files
breadcrumb-the-shire/web/css/components/app-tabs.css
fs e145b7d089 feat: introduce mathematical type scale with single-source-of-truth typography tokens
Define a major-third (1.25) type scale as CSS custom properties in the tokens
layer (--text-2xs through --text-5xl), along with line-height (--leading-*),
font-weight (--font-*), and letter-spacing (--tracking-*) tokens. Migrate all
28 core CSS files from ad-hoc values to token references with clamp()-based
responsive scaling for headings. Add TypographyTokenContractTest to enforce
token-only usage via grep-based assertions.

Task run: UI-TYPOGRAPHY-SCALE-001

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 15:47:55 +01:00

156 lines
4.2 KiB
CSS

@layer components {
/* Tab navigation — scrollable strip with chevron overflow, active/invalid states, no-JS fallback. */
.app-tabs {
display: flex;
flex-direction: column;
gap: var(--app-spacing);
}
/* No-JS fallback: show only first tab panel. */
html.no-js [data-tabs] [data-tab-panel] {
display: none;
}
html.no-js [data-tabs] [data-tab-panel]:first-of-type {
display: block;
}
/* JS mode: keep panels hidden until tab init completed (prevents flash). */
html.js [data-tabs]:not([data-tabs-ready="1"]) [data-tab-panel] {
display: none !important;
}
/* ------------------------------------------------------------------ */
/* Tab nav wrapper — holds optional chevron buttons + scrollable nav */
/* ------------------------------------------------------------------ */
.app-tabs-nav-wrap {
position: relative;
display: flex;
align-items: stretch;
border-bottom: 1px solid var(--tabs-border);
}
/* Chevron scroll buttons — styled like .outline.secondary.small */
.app-tabs-nav-wrap > .app-tabs-chevron {
flex: 0 0 auto;
display: none;
align-items: center;
justify-content: center;
border: 1px solid var(--app-border);
border-radius: var(--app-border-radius);
background-color: transparent;
color: var(--app-secondary);
font-size: var(--text-sm);
line-height: var(--leading-none);
cursor: pointer;
z-index: 1;
margin-block: auto;
transition:
background-color var(--app-transition),
border-color var(--app-transition),
color var(--app-transition);
}
.app-tabs-nav-wrap > .app-tabs-chevron:hover,
.app-tabs-nav-wrap > .app-tabs-chevron:focus-visible {
border-color: var(--app-secondary-hover-border);
color: var(--app-secondary-hover);
}
.app-tabs-nav-wrap > .app-tabs-chevron[disabled] {
opacity: 0.25;
cursor: default;
pointer-events: none;
}
.app-tabs-nav-wrap > .app-tabs-chevron--start {
order: -1;
margin-inline-end: 4px;
}
.app-tabs-nav-wrap > .app-tabs-chevron--end {
order: 1;
margin-inline-start: 4px;
}
/* Show chevrons when overflow is detected */
.app-tabs-nav-wrap.has-overflow > .app-tabs-chevron {
display: flex;
}
/* ------------------------------------------------------------------ */
/* The scrollable tab strip */
/* ------------------------------------------------------------------ */
.app-tabs-nav {
display: flex;
border-bottom: 1px solid var(--tabs-border); /* No-JS fallback border */
overflow-x: auto;
-webkit-overflow-scrolling: touch;
padding-block-start: 2px;
padding-left: 2px;
white-space: nowrap;
flex: 1 1 0%;
scroll-behavior: smooth;
}
/* When wrapped by JS, the wrapper owns the border */
.app-tabs-nav-wrap > .app-tabs-nav {
border-bottom: none;
}
/* When JS is active: hide the native scrollbar (chevrons take over) */
html.js .app-tabs-nav {
scrollbar-width: none; /* Firefox */
}
html.js .app-tabs-nav::-webkit-scrollbar {
display: none; /* Chrome / Safari / Edge */
}
.app-tabs-nav button,
.app-tabs-nav a {
background: transparent;
border: 0;
padding: 5px 10px 5px 10px;
color: var(--tabs-muted) !important;
cursor: pointer;
border-bottom: 2px solid transparent;
text-decoration: none;
border-radius: 0;
margin-bottom: 0;
}
.app-tabs-nav button:hover,
.app-tabs-nav button:focus-visible,
.app-tabs-nav a:hover,
.app-tabs-nav a:focus-visible {
--app-color: inherit;
color: var(--app-color) !important;
}
.app-tabs-nav button.is-active,
.app-tabs-nav a.is-active {
--app-color: inherit;
color: var(--app-color) !important;
border-color: var(--tabs-active);
}
.app-tabs-nav button.has-invalid,
.app-tabs-nav a.has-invalid {
color: var(--app-danger, #dc3545) !important;
}
[data-tab-panel][hidden] {
display: none;
}
html.js [data-tabs][data-tabs-ready="1"] [data-tab-panel]:not([hidden]) {
display: block;
}
/* Safety fallback when JS is available but the ready marker is missing. */
html:not(.no-js) [data-tabs] [data-tab-panel]:not([hidden]) {
display: block;
}
}