css-is-awesome 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +343 -0
- package/CHANGELOG.md +1036 -0
- package/CLAUDE.md +9 -0
- package/CONTRACT.md +580 -0
- package/GEMINI.md +9 -0
- package/LICENSE +21 -0
- package/LICENSE-third-party +88 -0
- package/MIGRATION.md +312 -0
- package/README.md +380 -0
- package/ROADMAP.md +714 -0
- package/THEMING.md +290 -0
- package/THREE-TIERS.md +158 -0
- package/VERSIONING.md +200 -0
- package/_index.scss +34 -0
- package/api.scss +16 -0
- package/bin/README.md +57 -0
- package/bin/cia.cjs +114 -0
- package/bin/migrate-bootstrap.cjs +427 -0
- package/bin/migrate-tailwind.cjs +832 -0
- package/css-is-awesome.instructions.md +533 -0
- package/dist/css-is-awesome.core.css +598 -0
- package/dist/css-is-awesome.core.min.css +1 -0
- package/dist/css-is-awesome.css +3733 -0
- package/dist/css-is-awesome.min.css +1 -0
- package/dist/css-is-awesome.utilities.css +2445 -0
- package/dist/css-is-awesome.utilities.min.css +1 -0
- package/dist/tokens.css +508 -0
- package/dist/tokens.d.ts +266 -0
- package/dist/tokens.min.css +1 -0
- package/figma-tokens/README.md +57 -0
- package/figma-tokens/primitives-brand.scss +121 -0
- package/figma-tokens/semantic-dark.scss +61 -0
- package/figma-tokens/semantic-light.scss +61 -0
- package/figma-tokens/tokens.json +144 -0
- package/llm.txt +183 -0
- package/mcp/server.cjs +1495 -0
- package/package.json +199 -0
- package/public/icons/README.md +278 -0
- package/public/icons/arrow-right.svg +4 -0
- package/public/icons/check.svg +4 -0
- package/public/icons/chevron-down.svg +4 -0
- package/public/icons/close.svg +4 -0
- package/public/icons/core/arrow-down.svg +13 -0
- package/public/icons/core/arrow-left.svg +13 -0
- package/public/icons/core/arrow-right.svg +13 -0
- package/public/icons/core/arrow-up.svg +13 -0
- package/public/icons/core/bell.svg +13 -0
- package/public/icons/core/calendar.svg +15 -0
- package/public/icons/core/check.svg +12 -0
- package/public/icons/core/chevron-down.svg +12 -0
- package/public/icons/core/chevron-left.svg +12 -0
- package/public/icons/core/chevron-right.svg +12 -0
- package/public/icons/core/chevron-up.svg +12 -0
- package/public/icons/core/clock.svg +13 -0
- package/public/icons/core/close.svg +13 -0
- package/public/icons/core/copy.svg +13 -0
- package/public/icons/core/download.svg +14 -0
- package/public/icons/core/edit.svg +13 -0
- package/public/icons/core/error.svg +14 -0
- package/public/icons/core/external-link.svg +14 -0
- package/public/icons/core/eye-off.svg +15 -0
- package/public/icons/core/eye.svg +13 -0
- package/public/icons/core/filter.svg +12 -0
- package/public/icons/core/heart.svg +12 -0
- package/public/icons/core/help.svg +14 -0
- package/public/icons/core/home.svg +13 -0
- package/public/icons/core/info.svg +14 -0
- package/public/icons/core/loading.svg +12 -0
- package/public/icons/core/lock.svg +13 -0
- package/public/icons/core/mail.svg +13 -0
- package/public/icons/core/menu.svg +14 -0
- package/public/icons/core/message.svg +12 -0
- package/public/icons/core/minus.svg +12 -0
- package/public/icons/core/more-horizontal.svg +14 -0
- package/public/icons/core/pause.svg +13 -0
- package/public/icons/core/play.svg +12 -0
- package/public/icons/core/plus.svg +13 -0
- package/public/icons/core/refresh.svg +15 -0
- package/public/icons/core/save.svg +14 -0
- package/public/icons/core/search.svg +13 -0
- package/public/icons/core/settings.svg +13 -0
- package/public/icons/core/share.svg +14 -0
- package/public/icons/core/sort.svg +15 -0
- package/public/icons/core/star.svg +12 -0
- package/public/icons/core/success.svg +13 -0
- package/public/icons/core/trash.svg +16 -0
- package/public/icons/core/unlock.svg +13 -0
- package/public/icons/core/upload.svg +14 -0
- package/public/icons/core/user.svg +13 -0
- package/public/icons/core/users.svg +15 -0
- package/public/icons/core/warning.svg +14 -0
- package/public/icons/download.svg +4 -0
- package/public/icons/edit.svg +4 -0
- package/public/icons/menu.svg +4 -0
- package/public/icons/search.svg +4 -0
- package/public/theme.css +4179 -0
- package/public/themes/README.md +102 -0
- package/public/themes/boilerplate/theme.css +154 -0
- package/public/themes/boilerplate-dark/theme.css +171 -0
- package/public/themes/boilerplate-light/theme.css +171 -0
- package/public/themes/cupertino/theme.css +165 -0
- package/public/themes/cupertino-dark/theme.css +200 -0
- package/public/themes/cupertino-light/theme.css +227 -0
- package/public/themes/glass/theme.css +190 -0
- package/public/themes/glass-dark/theme.css +201 -0
- package/public/themes/glass-light/theme.css +229 -0
- package/public/themes/graphite/theme.css +155 -0
- package/public/themes/graphite-dark/theme.css +223 -0
- package/public/themes/graphite-light/theme.css +191 -0
- package/public/themes/press/theme.css +189 -0
- package/public/themes/press-dark/theme.css +199 -0
- package/public/themes/press-light/theme.css +235 -0
- package/public/themes/prism/theme.css +159 -0
- package/public/themes/prism-dark/theme.css +185 -0
- package/public/themes/prism-light/theme.css +188 -0
- package/public/themes/sketchbook/theme.css +152 -0
- package/public/themes/sketchbook-dark/theme.css +194 -0
- package/public/themes/sketchbook-light/theme.css +188 -0
- package/public/themes/terminal/theme.css +159 -0
- package/public/themes/terminal-dark/theme.css +229 -0
- package/public/themes/terminal-light/theme.css +152 -0
- package/scripts/README.md +70 -0
- package/scripts/icon-contract.json +67 -0
- package/scripts/icon-validator.js +205 -0
- package/scripts/prepare-dist.mjs +65 -0
- package/scripts/theme-a11y.js +415 -0
- package/scripts/theme-contract.json +171 -0
- package/scripts/theme-validator.js +674 -0
- package/scss/README.md +55 -0
- package/scss/_animations-utilities.scss +107 -0
- package/scss/_animations.scss +248 -0
- package/scss/_app-styles.scss +15 -0
- package/scss/_generator.scss +174 -0
- package/scss/_icons.scss +475 -0
- package/scss/_index.scss +17 -0
- package/scss/_layout.scss +597 -0
- package/scss/_mixins.scss +1036 -0
- package/scss/_system.scss +291 -0
- package/scss/_utilities.scss +377 -0
- package/scss/api.scss +61 -0
- package/scss/components/_accordion.scss +72 -0
- package/scss/components/_buttons.scss +171 -0
- package/scss/components/_copy-button.scss +69 -0
- package/scss/components/_data.scss +190 -0
- package/scss/components/_feedback.scss +197 -0
- package/scss/components/_forms.scss +272 -0
- package/scss/components/_index.scss +17 -0
- package/scss/components/_navigation.scss +186 -0
- package/scss/components/_overlay.scss +259 -0
- package/scss/components/_stepper.scss +159 -0
- package/scss/components/_tabs.scss +72 -0
- package/scss/core.scss +54 -0
- package/scss/examples/_theming.scss +107 -0
- package/scss/examples/_usage.scss +317 -0
- package/scss/main.scss +64 -0
- package/scss/recipes/README.md +110 -0
- package/scss/recipes/_bare-tags.scss +232 -0
- package/scss/recipes/_recipe-template.md +119 -0
- package/scss/recipes/combobox.md +509 -0
- package/scss/recipes/dialog.md +265 -0
- package/scss/recipes/print-to-pdf.md +263 -0
- package/scss/theme/README.md +67 -0
- package/scss/theme/_brand.scss +18 -0
- package/scss/theme/_colors-dark.scss +68 -0
- package/scss/theme/_colors-light.scss +70 -0
- package/scss/theme/_components.scss +94 -0
- package/scss/theme/_icons.scss +104 -0
- package/scss/theme/_index.scss +62 -0
- package/scss/theme/_shadows.scss +65 -0
- package/scss/theme/_shape.scss +13 -0
- package/scss/theme/_typography.scss +9 -0
- package/scss/theme/registry.json +447 -0
- package/scss/themes/boilerplate-dark.scss +212 -0
- package/scss/themes/boilerplate-light.scss +209 -0
- package/scss/themes/boilerplate.scss +181 -0
- package/scss/themes/cupertino-dark.scss +238 -0
- package/scss/themes/cupertino-light.scss +266 -0
- package/scss/themes/cupertino.scss +194 -0
- package/scss/themes/glass-dark.scss +238 -0
- package/scss/themes/glass-light.scss +266 -0
- package/scss/themes/glass.scss +220 -0
- package/scss/themes/graphite-dark.scss +260 -0
- package/scss/themes/graphite-light.scss +228 -0
- package/scss/themes/graphite.scss +182 -0
- package/scss/themes/press-dark.scss +237 -0
- package/scss/themes/press-light.scss +273 -0
- package/scss/themes/press.scss +241 -0
- package/scss/themes/prism-dark.scss +217 -0
- package/scss/themes/prism-light.scss +220 -0
- package/scss/themes/prism.scss +190 -0
- package/scss/themes/sketchbook-dark.scss +231 -0
- package/scss/themes/sketchbook-light.scss +231 -0
- package/scss/themes/sketchbook.scss +178 -0
- package/scss/themes/terminal-dark.scss +267 -0
- package/scss/themes/terminal-light.scss +175 -0
- package/scss/themes/terminal.scss +185 -0
- package/scss/tokens.scss +23 -0
- package/scss/utilities-only.scss +4 -0
package/scss/api.scss
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// API - Authoring Barrel (zero-emit)
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Import this in a COMPONENT stylesheet to reach the whole mixin/function API
|
|
5
|
+
// WITHOUT emitting any CSS. Nothing is printed until you actually call a mixin.
|
|
6
|
+
//
|
|
7
|
+
// This is the entry point for per-component styles (e.g. Card.module.scss):
|
|
8
|
+
//
|
|
9
|
+
// @use 'css-is-awesome/api' as cia;
|
|
10
|
+
//
|
|
11
|
+
// .card {
|
|
12
|
+
// background: cia.color(surface-default);
|
|
13
|
+
// padding: cia.space(4);
|
|
14
|
+
// border-radius: cia.radius(md);
|
|
15
|
+
// @include cia.card-base($shadow: 2);
|
|
16
|
+
// }
|
|
17
|
+
//
|
|
18
|
+
// It emits ZERO bytes and NO `:root` block, so it is safe inside a
|
|
19
|
+
// `.module.scss` under Next.js CSS Modules "pure" mode.
|
|
20
|
+
//
|
|
21
|
+
// The GLOBAL tokens (`:root { --… }`) and animation keyframes are emitted ONCE
|
|
22
|
+
// at your app root by a separate import — the CSS bundle (`css-is-awesome`) or
|
|
23
|
+
// a theme stylesheet. Tokens + keyframes live at the root; each component only
|
|
24
|
+
// pulls in the mixins here. `cia.animate(fade-in)` emits an `animation-name`
|
|
25
|
+
// that references the globally-defined keyframe — the keyframe itself is NOT
|
|
26
|
+
// re-emitted per component (that is why `animations-utilities` is deliberately
|
|
27
|
+
// NOT forwarded here; see `_index.scss` for the kitchen-sink barrel).
|
|
28
|
+
//
|
|
29
|
+
// See also:
|
|
30
|
+
// - `css-is-awesome` → the CSS bundle (emits tokens + base, use once)
|
|
31
|
+
// - `css-is-awesome/tokens` → tokens-only CSS
|
|
32
|
+
// - `css-is-awesome/api` → THIS file: mixins/functions, zero emit
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
@use './system' as s;
|
|
36
|
+
@use './theme' as t;
|
|
37
|
+
|
|
38
|
+
// Forward the full authoring API. Every entry below is zero-emit: mixins and
|
|
39
|
+
// functions print nothing until called; token maps are compile-time only.
|
|
40
|
+
@forward './system' as sys-*;
|
|
41
|
+
@forward './theme' as theme-*;
|
|
42
|
+
@forward './generator';
|
|
43
|
+
@forward './mixins'; // No prefix - main API
|
|
44
|
+
@forward './layout'; // Layout mixins (grid, wrap, page)
|
|
45
|
+
@forward './icons' as icon-*; // Icon mixins with prefix
|
|
46
|
+
@forward './animations'; // animate() + animate-on() mixins (CSS-Modules-safe)
|
|
47
|
+
@forward './components'; // Component mixins (btn, card, nav, etc.)
|
|
48
|
+
|
|
49
|
+
// NOTE: './animations-utilities' (keyframes + .cia-anim-* classes + global
|
|
50
|
+
// reduced-motion safety net) is intentionally NOT forwarded here — it emits
|
|
51
|
+
// CSS and belongs in the global bundle, loaded once.
|
|
52
|
+
|
|
53
|
+
// Re-export key maps for advanced usage (variable declarations — no CSS).
|
|
54
|
+
$spacing: s.$spacing;
|
|
55
|
+
$breakpoints: s.$breakpoints;
|
|
56
|
+
$z-layers: s.$z-layers;
|
|
57
|
+
$font-sizes: s.$font-sizes;
|
|
58
|
+
$theme-brand: t.$theme-brand;
|
|
59
|
+
$theme-light: t.$theme-light;
|
|
60
|
+
$theme-dark: t.$theme-dark;
|
|
61
|
+
$theme-radius: t.$theme-radius;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// ACCORDION
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Native <details name="..."> mutual exclusion. Zero JavaScript.
|
|
5
|
+
//
|
|
6
|
+
// Usage (mixin — the primary API):
|
|
7
|
+
// .faq-item { @include accordion; }
|
|
8
|
+
//
|
|
9
|
+
// Usage (utility class, opt-in convenience):
|
|
10
|
+
// <details name="faq" class="cia-accordion">
|
|
11
|
+
// <summary>Question</summary>
|
|
12
|
+
// <div>Answer</div>
|
|
13
|
+
// </details>
|
|
14
|
+
//
|
|
15
|
+
// Multiple <details name="faq"> at the same DOM level auto-close siblings
|
|
16
|
+
// when one opens. Native browser behavior; no JS, no script tag, no hydration.
|
|
17
|
+
// Baseline since 2024 (Chrome 120, Firefox 130, Safari 17.6).
|
|
18
|
+
//
|
|
19
|
+
// A11y: <details>/<summary> is keyboard-accessible by default (Tab + Space).
|
|
20
|
+
// focus-ring routed through m.focus-ring for consistency.
|
|
21
|
+
// ============================================================================
|
|
22
|
+
@use '../mixins' as m;
|
|
23
|
+
|
|
24
|
+
@mixin accordion {
|
|
25
|
+
border: 1px solid m.color(border-default);
|
|
26
|
+
border-radius: m.radius(md);
|
|
27
|
+
background: m.color(surface-default);
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
|
|
30
|
+
& + & { margin-block-start: m.space(2); }
|
|
31
|
+
|
|
32
|
+
> summary {
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
padding: m.space(3) m.space(4);
|
|
35
|
+
font-weight: m.font-weight(medium);
|
|
36
|
+
color: m.color(text-primary);
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: space-between;
|
|
40
|
+
gap: m.space(2);
|
|
41
|
+
list-style: none;
|
|
42
|
+
min-height: var(--touch-target-min, 24px); /* WCAG 2.2 SC 2.5.8 */
|
|
43
|
+
@include m.transition(background-color);
|
|
44
|
+
|
|
45
|
+
&::-webkit-details-marker { display: none; } /* Safari */
|
|
46
|
+
|
|
47
|
+
&::after {
|
|
48
|
+
content: '';
|
|
49
|
+
width: 0.55em;
|
|
50
|
+
height: 0.55em;
|
|
51
|
+
border-right: 2px solid currentColor;
|
|
52
|
+
border-bottom: 2px solid currentColor;
|
|
53
|
+
transform: rotate(45deg);
|
|
54
|
+
transition: transform var(--duration-fast, 180ms) var(--ease, ease);
|
|
55
|
+
flex-shrink: 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&:hover { background: m.color(interactive-hover); }
|
|
59
|
+
|
|
60
|
+
@include m.focus-ring;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
&[open] > summary::after {
|
|
64
|
+
transform: rotate(-135deg);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
> :not(summary) {
|
|
68
|
+
padding: m.space(3) m.space(4);
|
|
69
|
+
padding-block-start: 0;
|
|
70
|
+
color: m.color(text-secondary);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// BUTTONS
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Mixin-first button system. One router (`btn`) covers every variant.
|
|
5
|
+
// Each variant is a small private mixin; the router stays small no matter
|
|
6
|
+
// how many variants you add.
|
|
7
|
+
//
|
|
8
|
+
// Numeric scale keys are the source of truth (1..9). T-shirt keys still
|
|
9
|
+
// work as aliases — both of the following produce identical output:
|
|
10
|
+
// .btn-small-numeric { @include btn-base($py: 1, $px: 2, $font-size: 2); }
|
|
11
|
+
// .btn-small-tshirt { @include btn-base($py: xs, $px: sm, $font-size: sm); }
|
|
12
|
+
//
|
|
13
|
+
// Usage:
|
|
14
|
+
// .btn { @include btn-base; }
|
|
15
|
+
// .btn-cta { @include btn(primary, $px: 6, $r: full); }
|
|
16
|
+
// .btn-warn { @include btn(warning); }
|
|
17
|
+
// .btn-edit { @include btn(outline) { letter-spacing: 0.05em; } }
|
|
18
|
+
//
|
|
19
|
+
// To add a new variant: write a `_btn-<name>` private mixin below and add
|
|
20
|
+
// one branch to the router. Status variants (info/success/warning/error)
|
|
21
|
+
// are FREE — they share `_btn-status` via token interpolation.
|
|
22
|
+
// ============================================================================
|
|
23
|
+
@use 'sass:map';
|
|
24
|
+
@use '../theme' as t;
|
|
25
|
+
@use '../system' as s;
|
|
26
|
+
@use '../mixins' as m;
|
|
27
|
+
|
|
28
|
+
// ----------------------------------------------------------------------------
|
|
29
|
+
// BASE — shared structural rules (display, padding, radius, focus-ring, …)
|
|
30
|
+
// ----------------------------------------------------------------------------
|
|
31
|
+
@mixin btn-base(
|
|
32
|
+
$py: 1,
|
|
33
|
+
$px: 4,
|
|
34
|
+
$r: md,
|
|
35
|
+
$font-weight: medium,
|
|
36
|
+
$font-size: null
|
|
37
|
+
) {
|
|
38
|
+
@include m.button-reset;
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
gap: m.space(1);
|
|
42
|
+
min-height: var(--touch-target-min, 24px); /* WCAG 2.2 SC 2.5.8 */
|
|
43
|
+
font-weight: var(--btn-font-weight, #{m.font-weight($font-weight)});
|
|
44
|
+
padding: var(--btn-padding-y, #{m.space($py)}) var(--btn-padding-x, #{m.space($px)});
|
|
45
|
+
border-radius: var(--btn-radius, #{m.radius($r)});
|
|
46
|
+
@include m.transition(background-color, border-color, color, box-shadow);
|
|
47
|
+
@include m.focus-ring;
|
|
48
|
+
|
|
49
|
+
@if $font-size {
|
|
50
|
+
font-size: var(--btn-font-size, #{m.font-size($font-size)});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ----------------------------------------------------------------------------
|
|
55
|
+
// ROUTER — btn($variant)
|
|
56
|
+
// ----------------------------------------------------------------------------
|
|
57
|
+
// One mixin handles every variant. Defaults come from the theme contract;
|
|
58
|
+
// every token is overridable via named args. `$args...` forwards to
|
|
59
|
+
// btn-base for sizing (`$py`, `$px`, `$r`, `$font-weight`, `$font-size`).
|
|
60
|
+
@mixin btn(
|
|
61
|
+
$variant,
|
|
62
|
+
$bg: null, $bg-hover: null, $bg-active: null,
|
|
63
|
+
$color: null, $border: null,
|
|
64
|
+
$args...
|
|
65
|
+
) {
|
|
66
|
+
@include btn-base($args...);
|
|
67
|
+
|
|
68
|
+
@if $variant == primary { @include _btn-solid(action-primary-default, action-primary-hover, action-primary-active, $bg, $bg-hover, $bg-active, $color); } @else if $variant == secondary { @include _btn-solid(action-secondary-default, action-secondary-hover, action-secondary-active, $bg, $bg-hover, $bg-active, $color); } @else if $variant == outline { @include _btn-outline($color, $border); } @else if $variant == ghost { @include _btn-ghost($color); } @else if $variant == info or
|
|
69
|
+
$variant == success or
|
|
70
|
+
$variant == warning or
|
|
71
|
+
$variant == error { @include _btn-status($variant, $bg, $border, $color); } @else if $variant == disabled { @include _btn-disabled($bg, $border); } @else if $variant == action { @include _btn-status(info, $bg, $border, $color); }
|
|
72
|
+
// back-compat alias for old buttonold
|
|
73
|
+
@else {
|
|
74
|
+
@error "btn(): unknown variant '#{$variant}'. Expected one of: primary, secondary, outline, ghost, info, success, warning, error, disabled, action.";
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@content;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ----------------------------------------------------------------------------
|
|
81
|
+
// PER-VARIANT PRIVATE MIXINS — small, local, easy to grow.
|
|
82
|
+
// ----------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
// Solid variants — same shape, different default tokens. Used by primary,
|
|
85
|
+
// secondary, and any future "filled" variant (e.g. tertiary).
|
|
86
|
+
//
|
|
87
|
+
// State cascade: when consumer passes only $bg (custom palette), hover +
|
|
88
|
+
// active derive from $bg via the same color-mix(in oklch) formula m.states()
|
|
89
|
+
// uses (12% hover, 20% active, light-dark-aware). Explicit $bg-hover /
|
|
90
|
+
// $bg-active still take precedence when supplied. No $bg = original behavior
|
|
91
|
+
// (use $hover / $active token defaults).
|
|
92
|
+
@mixin _btn-solid(
|
|
93
|
+
$default, $hover, $active,
|
|
94
|
+
$bg: null, $bg-hover: null, $bg-active: null, $color: null
|
|
95
|
+
) {
|
|
96
|
+
background: m.color($bg or $default);
|
|
97
|
+
color: m.color($color or text-inverse);
|
|
98
|
+
|
|
99
|
+
&:hover {
|
|
100
|
+
@if $bg-hover {
|
|
101
|
+
background: m.color($bg-hover);
|
|
102
|
+
} @else if $bg {
|
|
103
|
+
background: color-mix(in oklch, m.color($bg), light-dark(black, white) 12%);
|
|
104
|
+
} @else {
|
|
105
|
+
background: m.color($hover);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
&:active {
|
|
110
|
+
@if $bg-active {
|
|
111
|
+
background: m.color($bg-active);
|
|
112
|
+
} @else if $bg {
|
|
113
|
+
background: color-mix(in oklch, m.color($bg), light-dark(black, white) 20%);
|
|
114
|
+
} @else {
|
|
115
|
+
background: m.color($active);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
&:disabled, &[disabled] { @include m.disabled; }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Outline variant — transparent bg, visible border, hover bg + border swap.
|
|
123
|
+
@mixin _btn-outline($color: null, $border: null) {
|
|
124
|
+
background: transparent;
|
|
125
|
+
border: 1px solid m.color($border or border-default);
|
|
126
|
+
color: m.color($color or text-primary);
|
|
127
|
+
&:hover {
|
|
128
|
+
background: m.color(interactive-hover);
|
|
129
|
+
border-color: m.color(border-focus);
|
|
130
|
+
}
|
|
131
|
+
&:active { background: m.color(interactive-active); }
|
|
132
|
+
&:disabled, &[disabled] { @include m.disabled; }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Ghost variant — transparent bg, no border, hover bg only.
|
|
136
|
+
@mixin _btn-ghost($color: null) {
|
|
137
|
+
background: transparent;
|
|
138
|
+
color: m.color($color or text-primary);
|
|
139
|
+
&:hover { background: m.color(interactive-hover); }
|
|
140
|
+
&:active { background: m.color(interactive-active); }
|
|
141
|
+
&:disabled, &[disabled] { @include m.disabled; }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Status variants — token interpolation. Adding a new status is FREE if the
|
|
145
|
+
// tokens exist in the contract: <status>-subtle, <status>-default, <status>-text.
|
|
146
|
+
@mixin _btn-status($status, $bg: null, $border: null, $color: null) {
|
|
147
|
+
background: m.color($bg or #{$status}-subtle);
|
|
148
|
+
border: 1px solid m.color($border or #{$status}-default);
|
|
149
|
+
color: m.color($color or #{$status}-text);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Disabled state — looks disabled AND disables interaction.
|
|
153
|
+
@mixin _btn-disabled($bg: null, $border: null) {
|
|
154
|
+
background: m.color($bg or surface-muted);
|
|
155
|
+
border: 1px solid m.color($border or border-default);
|
|
156
|
+
@include m.disabled;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ----------------------------------------------------------------------------
|
|
160
|
+
// ICON BUTTON — different shape (square, no padding system), kept separate.
|
|
161
|
+
// ----------------------------------------------------------------------------
|
|
162
|
+
@mixin btn-icon($size: 2.5rem, $r: md) {
|
|
163
|
+
@include m.button-reset;
|
|
164
|
+
@include m.flex($justify: center);
|
|
165
|
+
width: $size;
|
|
166
|
+
height: $size;
|
|
167
|
+
border-radius: m.radius($r);
|
|
168
|
+
@include m.transition(background-color, color);
|
|
169
|
+
@include m.focus-ring;
|
|
170
|
+
@include m.interactive;
|
|
171
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// COPY BUTTON
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Reusable copy-to-clipboard button. Decoupled from any parent context;
|
|
5
|
+
// consumer wires it to any element via `data-copy-target="#id"`.
|
|
6
|
+
//
|
|
7
|
+
// Usage (mixin — primary API):
|
|
8
|
+
// .doc-copy { @include copy-button; }
|
|
9
|
+
//
|
|
10
|
+
// Usage (utility class):
|
|
11
|
+
// <pre id="snippet">…</pre>
|
|
12
|
+
// <button data-copy-target="#snippet" class="cia-copy-button">Copy</button>
|
|
13
|
+
//
|
|
14
|
+
// cia ships ZERO JavaScript in the npm package. Wire your own click handler
|
|
15
|
+
// to read `data-copy-target` and call navigator.clipboard.writeText().
|
|
16
|
+
//
|
|
17
|
+
// Copy-paste recipe + the framework-free shim are documented at
|
|
18
|
+
// /docs/recipes/copy-button on the cia site. A separate add-on package
|
|
19
|
+
// (@cia/copy-button) is on the v1.x roadmap.
|
|
20
|
+
//
|
|
21
|
+
// Toast variant: pair the button with <div popover="hint" class="cia-copy-toast">
|
|
22
|
+
// and add `popovertarget="..."` to the button. Native popover handles show;
|
|
23
|
+
// your handler calls hidePopover() after a timeout for auto-hide.
|
|
24
|
+
// ============================================================================
|
|
25
|
+
@use '../mixins' as m;
|
|
26
|
+
@use './overlay' as o;
|
|
27
|
+
|
|
28
|
+
@mixin copy-button {
|
|
29
|
+
display: inline-flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
gap: m.space(1);
|
|
32
|
+
padding: m.space(1) m.space(2);
|
|
33
|
+
min-height: var(--touch-target-min, 24px); /* WCAG 2.2 SC 2.5.8 */
|
|
34
|
+
font-family: m.font-family(mono);
|
|
35
|
+
font-size: m.font-size(2);
|
|
36
|
+
line-height: 1.2;
|
|
37
|
+
letter-spacing: 0.04em;
|
|
38
|
+
color: m.color(text-secondary);
|
|
39
|
+
background: m.color(surface-raised);
|
|
40
|
+
border: 1px solid m.color(border-default);
|
|
41
|
+
border-radius: m.radius(sm);
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
@include m.transition(background-color, color, border-color);
|
|
44
|
+
@include m.focus-ring;
|
|
45
|
+
|
|
46
|
+
&:hover {
|
|
47
|
+
background: m.color(surface-emphasis);
|
|
48
|
+
color: m.color(text-primary);
|
|
49
|
+
border-color: m.color(border-emphasis);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* Copied state — JS shim toggles data-copied="true" for 1.5s */
|
|
53
|
+
&[data-copied='true'] {
|
|
54
|
+
background: m.color(success-subtle);
|
|
55
|
+
color: m.color(success-text);
|
|
56
|
+
border-color: m.color(success-default);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Toast variant — composes with the tooltip mixin from overlay.
|
|
61
|
+
// Used as a separate element triggered by the copy button via popovertarget.
|
|
62
|
+
@mixin copy-toast {
|
|
63
|
+
@include o.tooltip;
|
|
64
|
+
background: m.color(success-default);
|
|
65
|
+
color: m.color(text-inverse);
|
|
66
|
+
font-family: m.font-family(primary);
|
|
67
|
+
font-size: m.font-size(2);
|
|
68
|
+
white-space: normal;
|
|
69
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// DATA DISPLAY — Table, Card, List, Badge, Avatar
|
|
3
|
+
// ============================================================================
|
|
4
|
+
@use 'sass:map';
|
|
5
|
+
@use '../theme' as t;
|
|
6
|
+
@use '../system' as s;
|
|
7
|
+
@use '../mixins' as m;
|
|
8
|
+
|
|
9
|
+
// ----------------------------------------------------------------------------
|
|
10
|
+
// BADGE — moved here from _feedback.scss in v0.8.1 (alongside card / table).
|
|
11
|
+
// ----------------------------------------------------------------------------
|
|
12
|
+
@mixin badge-base(
|
|
13
|
+
$py: 1,
|
|
14
|
+
$px: 1,
|
|
15
|
+
$r: full,
|
|
16
|
+
$font-size: 1
|
|
17
|
+
) {
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
padding: var(--badge-padding-y, #{m.space($py)}) var(--badge-padding-x, #{m.space($px)});
|
|
21
|
+
border-radius: var(--badge-radius, #{m.radius($r)});
|
|
22
|
+
@include m.font(medium, $font-size);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@mixin badge($status: info) {
|
|
26
|
+
@include badge-base;
|
|
27
|
+
background: m.color(#{$status}-subtle);
|
|
28
|
+
color: m.color(#{$status}-text);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ----------------------------------------------------------------------------
|
|
32
|
+
// TABLE
|
|
33
|
+
// ----------------------------------------------------------------------------
|
|
34
|
+
// Usage:
|
|
35
|
+
// table { @include table-base; }
|
|
36
|
+
// table { @include table-base($striped: true, $hover: true); }
|
|
37
|
+
|
|
38
|
+
@mixin table-base($striped: false, $hover: false, $bordered: false, $compact: false) {
|
|
39
|
+
width: 100%;
|
|
40
|
+
border-collapse: collapse;
|
|
41
|
+
@include m.font(reg, 2);
|
|
42
|
+
|
|
43
|
+
th, td {
|
|
44
|
+
text-align: left;
|
|
45
|
+
padding: m.space(if(sass($compact): 1; else: 2)) m.space(if(sass($compact): 2; else: 4));
|
|
46
|
+
border-bottom: 1px solid m.color(border-default);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
th {
|
|
50
|
+
@include m.font(semibold, 1);
|
|
51
|
+
color: m.color(text-secondary);
|
|
52
|
+
text-transform: uppercase;
|
|
53
|
+
letter-spacing: map.get(s.$letter-spacings, wide);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@if $bordered {
|
|
57
|
+
th, td { border: 1px solid m.color(border-default); }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@if $striped {
|
|
61
|
+
tbody tr:nth-child(even) {
|
|
62
|
+
background: m.color(surface-subtle);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@if $hover {
|
|
67
|
+
tbody tr {
|
|
68
|
+
@include m.transition(background-color);
|
|
69
|
+
&:hover { background: m.color(interactive-hover); }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Responsive table wrapper
|
|
75
|
+
@mixin table-responsive {
|
|
76
|
+
width: 100%;
|
|
77
|
+
overflow-x: auto;
|
|
78
|
+
-webkit-overflow-scrolling: touch;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ----------------------------------------------------------------------------
|
|
82
|
+
// CARD
|
|
83
|
+
// ----------------------------------------------------------------------------
|
|
84
|
+
@mixin card-base(
|
|
85
|
+
$p: 4,
|
|
86
|
+
$r: lg,
|
|
87
|
+
$shadow: 1,
|
|
88
|
+
$bg: surface-default,
|
|
89
|
+
$border: false
|
|
90
|
+
) {
|
|
91
|
+
padding: var(--card-padding, #{m.space($p)});
|
|
92
|
+
border-radius: var(--card-radius, #{m.radius($r)});
|
|
93
|
+
background: m.color($bg);
|
|
94
|
+
|
|
95
|
+
@if $shadow and $shadow != none {
|
|
96
|
+
box-shadow: var(--card-shadow, #{m.shadow($shadow)});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@if $border {
|
|
100
|
+
border: var(--card-border, 1px solid #{m.color(border-default)});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@mixin card-header($pb: 2) {
|
|
105
|
+
padding-block-end: m.space($pb);
|
|
106
|
+
border-block-end: 1px solid m.color(border-default);
|
|
107
|
+
margin-block-end: m.space(4);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@mixin card-footer($pt: 2) {
|
|
111
|
+
padding-block-start: m.space($pt);
|
|
112
|
+
border-block-start: 1px solid m.color(border-default);
|
|
113
|
+
margin-block-start: m.space(4);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@mixin card-interactive {
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
@include m.transition(box-shadow, transform);
|
|
119
|
+
|
|
120
|
+
&:hover {
|
|
121
|
+
box-shadow: m.shadow(2);
|
|
122
|
+
transform: translateY(-1px);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
&:active {
|
|
126
|
+
transform: translateY(0);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ----------------------------------------------------------------------------
|
|
131
|
+
// LIST
|
|
132
|
+
// ----------------------------------------------------------------------------
|
|
133
|
+
@mixin list-base($gap: 0, $dividers: false) {
|
|
134
|
+
@include m.list-reset;
|
|
135
|
+
display: flex;
|
|
136
|
+
flex-direction: column;
|
|
137
|
+
gap: m.space($gap);
|
|
138
|
+
|
|
139
|
+
@if $dividers and $gap == 0 {
|
|
140
|
+
> li + li { border-top: 1px solid m.color(border-default); }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Usage:
|
|
145
|
+
// li { @include list-item; }
|
|
146
|
+
// li { @include list-item($interactive: true); }
|
|
147
|
+
@mixin list-item($py: 2, $px: 4, $interactive: false) {
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
gap: m.space(2);
|
|
151
|
+
padding: m.space($py) m.space($px);
|
|
152
|
+
|
|
153
|
+
@if $interactive {
|
|
154
|
+
cursor: pointer;
|
|
155
|
+
@include m.transition(background-color);
|
|
156
|
+
&:hover { background: m.color(interactive-hover); }
|
|
157
|
+
&:active { background: m.color(interactive-active); }
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ----------------------------------------------------------------------------
|
|
162
|
+
// AVATAR
|
|
163
|
+
// ----------------------------------------------------------------------------
|
|
164
|
+
@mixin avatar($size: 2.5rem, $r: full) {
|
|
165
|
+
width: $size;
|
|
166
|
+
height: $size;
|
|
167
|
+
border-radius: m.radius($r);
|
|
168
|
+
object-fit: cover;
|
|
169
|
+
flex-shrink: 0;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@mixin avatar-placeholder($size: 2.5rem, $r: full, $bg: surface-muted, $color: text-muted) {
|
|
173
|
+
@include m.flex($justify: center);
|
|
174
|
+
width: $size;
|
|
175
|
+
height: $size;
|
|
176
|
+
border-radius: m.radius($r);
|
|
177
|
+
background: m.color($bg);
|
|
178
|
+
color: m.color($color);
|
|
179
|
+
@include m.font(semibold, 2);
|
|
180
|
+
flex-shrink: 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@mixin avatar-group($overlap: -0.5rem) {
|
|
184
|
+
display: flex;
|
|
185
|
+
|
|
186
|
+
> * + * {
|
|
187
|
+
margin-left: $overlap;
|
|
188
|
+
border: 2px solid m.color(surface-default);
|
|
189
|
+
}
|
|
190
|
+
}
|