@mastors/core 1.1.0 → 2.0.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.
Files changed (50) hide show
  1. package/README.md +12 -121
  2. package/dist/mastors-core.css +62 -1615
  3. package/dist/mastors-core.css.map +1 -1
  4. package/package.json +6 -8
  5. package/scss/abstracts/_maps.scss +9 -0
  6. package/scss/accessibility/_index.scss +0 -1
  7. package/scss/accessibility/_screen-reader.scss +1 -4
  8. package/scss/api/_index.scss +0 -1
  9. package/scss/base/_reset.scss +1 -1
  10. package/scss/config/_flags.scss +9 -9
  11. package/scss/functions/_color.scss +15 -32
  12. package/scss/functions/_em.scss +2 -2
  13. package/scss/functions/_index.scss +0 -6
  14. package/scss/functions/_math.scss +0 -19
  15. package/scss/functions/_string.scss +0 -35
  16. package/scss/generators/_class-generator.scss +42 -31
  17. package/scss/generators/_responsive-generator.scss +4 -0
  18. package/scss/helpers/_visually-hidden.scss +14 -27
  19. package/scss/mixins/_breakpoint.scss +3 -3
  20. package/scss/mixins/_transition.scss +1 -1
  21. package/scss/responsive/_engine.scss +5 -3
  22. package/scss/responsive/_fluid-type.scss +9 -8
  23. package/scss/semantic/_colors.scss +15 -15
  24. package/scss/semantic/_spacing.scss +5 -5
  25. package/scss/semantic/_typography.scss +5 -5
  26. package/scss/themes/_base-theme.scss +5 -0
  27. package/scss/themes/_dark.scss +7 -8
  28. package/scss/tokens/_color.scss +4 -8
  29. package/scss/tokens/_shadows.scss +9 -7
  30. package/scss/tokens/_sizing.scss +6 -3
  31. package/scss/tokens/_typography.scss +10 -9
  32. package/scss/utilities/_borders.scss +6 -4
  33. package/scss/utilities/_colors.scss +1 -1
  34. package/scss/utilities/_index.scss +0 -4
  35. package/scss/utilities/_sizing.scss +2 -2
  36. package/scss/variables/_global.scss +4 -4
  37. package/scss/variables/_grid.scss +3 -3
  38. package/scss/vendors/_index.scss +2 -0
  39. package/src/index.ts +11 -0
  40. package/src/tokens.ts +314 -0
  41. package/src/types.ts +52 -0
  42. package/postinstall.js +0 -41
  43. package/scripts/generate-tokens.js +0 -259
  44. package/scss/accessibility/_print.scss +0 -52
  45. package/scss/config/_index.scss +0 -12
  46. package/scss/functions/_vars.scss +0 -49
  47. package/scss/utilities/_animation.scss +0 -125
  48. package/scss/utilities/_interaction.scss +0 -156
  49. package/scss/utilities/_layout.scss +0 -162
  50. package/scss/utilities/_typography.scss +0 -163
@@ -3,7 +3,6 @@
3
3
  // ─────────────────────────────────────────────────────────────
4
4
 
5
5
  @use "sass:color";
6
- @use "sass:list";
7
6
  @use "sass:math";
8
7
  @use "../tokens/color" as ct;
9
8
 
@@ -23,6 +22,21 @@
23
22
  }
24
23
 
25
24
  /// Return black or white for maximum contrast against $bg.
25
+ ///
26
+ /// Method: simplified linear luminance (no sRGB gamma expansion).
27
+ /// The three RGB coefficients (0.2126 / 0.7152 / 0.0722) are the WCAG
28
+ /// standard weights, but they are meant to be applied to *linearised*
29
+ /// channel values (after the 0.04045 piecewise inverse-gamma step).
30
+ /// Skipping gamma expansion makes mid-tones appear lighter than they
31
+ /// really are, so the WCAG threshold (0.179) would incorrectly return
32
+ /// $dark for many mid-grey backgrounds.
33
+ ///
34
+ /// To compensate, the threshold is adjusted to ~0.35 — empirically
35
+ /// calibrated for the non-linearised approximation used here. This
36
+ /// gives correct light/dark decisions across the full neutral scale.
37
+ ///
38
+ /// Do NOT use the return value for strict WCAG 2.1 contrast-ratio
39
+ /// calculations; implement full sRGB linearisation for that use case.
26
40
  @function contrast($bg, $light: white, $dark: black) {
27
41
  $luminance: 0.2126 * color.channel($bg, "red", $space: rgb) / 255
28
42
  + 0.7152 * color.channel($bg, "green", $space: rgb) / 255
@@ -33,34 +47,3 @@
33
47
  @return $light;
34
48
  }
35
49
  }
36
-
37
- /// Quick palette accessor — shorthand for ct.color()
38
- @function palette($name, $shade: null) {
39
- @return ct.color($name, $shade);
40
- }
41
-
42
- /// Emit a color as rgb() with optional opacity.
43
- @function rgb-color($name, $shade, $opacity: 1) {
44
- $c: ct.color($name, $shade);
45
- @if $opacity == 1 { @return $c; }
46
- $r: color.channel($c, "red", $space: rgb);
47
- $g: color.channel($c, "green", $space: rgb);
48
- $b: color.channel($c, "blue", $space: rgb);
49
- @return rgb($r $g $b / $opacity);
50
- }
51
-
52
- /// Create a stepped tint/shade ramp on the fly.
53
- @function color-ramp($base, $steps: 5, $dir: "tint") {
54
- $result: ();
55
- @for $i from 1 through $steps {
56
- $pct: math.div($i * 100%, $steps + 1);
57
- $step: null;
58
- @if $dir == "tint" {
59
- $step: tint($base, $pct);
60
- } @else {
61
- $step: shade($base, $pct);
62
- }
63
- $result: list.append($result, $step, comma);
64
- }
65
- @return $result;
66
- }
@@ -10,14 +10,14 @@
10
10
  /// @param {Number} $context - context font size in px (default: base)
11
11
  /// @return {Number} em value
12
12
  @function em($px, $context: vars.$base-font-size) {
13
- $base: null;
13
+ $base: 0;
14
14
  @if math.compatible($px, 1px) {
15
15
  $base: math.div($px, 1px);
16
16
  } @else {
17
17
  $base: $px;
18
18
  }
19
19
 
20
- $ctx: null;
20
+ $ctx: 0;
21
21
  @if math.compatible($context, 1px) {
22
22
  $ctx: math.div($context, 1px);
23
23
  } @else {
@@ -1,11 +1,5 @@
1
1
  // functions/_index.scss
2
2
  // ─────────────────────────────────────────────────────────────
3
- // NOTE: _vars.scss is intentionally NOT forwarded here.
4
- // _string.scss already defines vars() and vars-or().
5
- // _vars.scss defines a single-token vars($token, $fallback) variant
6
- // under a different signature — forwarding both causes a "two modules
7
- // define vars" conflict. Consumers who need the _vars.scss variant
8
- // should @use it directly.
9
3
 
10
4
  @forward "color";
11
5
  @forward "math";
@@ -19,22 +19,3 @@
19
19
  $intercept: $min-val - $slope * $min-vw;
20
20
  @return clamp(#{$min-val}, #{$slope * 100vw} + #{$intercept}, #{$max-val});
21
21
  }
22
-
23
- /// Strip the unit from a number: strip-unit(16px) → 16
24
- @function strip-unit($number) {
25
- @return math.div($number, ($number * 0 + 1));
26
- }
27
-
28
- /// Round to N decimal places: round-to(3.14159, 2) → 3.14
29
- @function round-to($number, $decimals: 2) {
30
- $n: math.pow(10, $decimals);
31
- @return math.div(math.round($number * $n), $n);
32
- }
33
-
34
- /// Linear interpolation between two values
35
- /// @param {Number} $a - start value
36
- /// @param {Number} $b - end value
37
- /// @param {Number} $t - interpolation factor (0–1)
38
- @function lerp($a, $b, $t) {
39
- @return $a + ($b - $a) * $t;
40
- }
@@ -20,38 +20,3 @@
20
20
  @function to-string($value) {
21
21
  @return #{$value};
22
22
  }
23
-
24
- /// Reference any design token as a CSS custom property (var()).
25
- /// Builds the --mastors-{category}-{keys...} expression at zero runtime cost.
26
- ///
27
- /// Usage:
28
- /// color: vars(color, primary, 500); → var(--mastors-color-primary-500)
29
- /// padding: vars(spacing, 4); → var(--mastors-spacing-4)
30
- /// border-radius: vars(radius, lg); → var(--mastors-radius-lg)
31
- /// transition-duration:vars(duration, 200); → var(--mastors-duration-200)
32
- /// z-index: vars(z, modal); → var(--mastors-z-modal)
33
- /// box-shadow: vars(shadow, md); → var(--mastors-shadow-md)
34
- ///
35
- /// @param {String} $category - Token category prefix (color, spacing, radius…)
36
- /// @param {ArgList} $keys - One or more path segments
37
- /// @return {String} - CSS var() expression
38
- @function vars($category, $keys...) {
39
- $prop: "--mastors-#{$category}";
40
- @each $key in $keys {
41
- $prop: "#{$prop}-#{$key}";
42
- }
43
- @return var(#{$prop});
44
- }
45
-
46
- /// vars() with a fallback value.
47
- /// Usage: color: vars-or(color, #3b82f6, primary, 500);
48
- /// @param {String} $category - Token category prefix
49
- /// @param {*} $fallback - Fallback value if custom property is not set
50
- /// @param {ArgList} $keys - Path segments
51
- @function vars-or($category, $fallback, $keys...) {
52
- $prop: "--mastors-#{$category}";
53
- @each $key in $keys {
54
- $prop: "#{$prop}-#{$key}";
55
- }
56
- @return var(#{$prop}, #{$fallback});
57
- }
@@ -10,13 +10,34 @@
10
10
  @use "../variables/breakpoints" as bpvars;
11
11
 
12
12
  /// Core utility class generator engine.
13
+ /// Emits base utility classes AND, for any entry with responsive: true,
14
+ /// also emits breakpoint-prefixed variants via the built-in responsive pass.
15
+ ///
16
+ /// @param {Map} $utilities - map of utility configs, each with:
17
+ /// - property {String} CSS property name
18
+ /// - values {Map} class-suffix → value pairs
19
+ /// - prefix {String} optional class prefix (e.g. "flex")
20
+ /// - responsive {Boolean} emit responsive variants (default false)
21
+ ///
22
+ /// Usage:
23
+ /// @include generate-utilities((
24
+ /// "flex-direction": (
25
+ /// property: flex-direction,
26
+ /// prefix: "flex",
27
+ /// values: ("row": row, "col": column, "row-reverse": row-reverse),
28
+ /// responsive: true,
29
+ /// ),
30
+ /// ));
13
31
  @mixin generate-utilities($utilities) {
14
32
  $cls-prefix: cfg.config("prefix");
15
- $use-important: cfg.config("important");
33
+ $important: null;
34
+ @if cfg.config("important") {
35
+ $important: !important;
36
+ }
16
37
 
17
38
  // ── Pass 1: base (unprefixed) classes ──────────────────────
18
39
  @each $name, $config in $utilities {
19
- $prop: map.get($config, property);
40
+ $prop: map.get($config, property);
20
41
  $values: map.get($config, values);
21
42
  $prefix: map.get($config, prefix);
22
43
 
@@ -26,52 +47,42 @@
26
47
  $class-name: "#{$cls-prefix}#{$prefix}-#{$suffix}";
27
48
  }
28
49
  .#{$class-name} {
29
- @if $use-important {
30
- #{$prop}: $value !important;
31
- } @else {
32
- #{$prop}: $value;
33
- }
50
+ #{$prop}: $value $important;
34
51
  }
35
52
  }
36
53
  }
37
54
 
38
55
  // ── Pass 2: responsive variants (.sm\:, .md\:, …) ─────────
56
+ // Only runs when $enable-responsive is true and at least one
57
+ // utility in the map declares responsive: true.
39
58
  @if flags.$enable-responsive {
40
59
  @each $bp-key, $bp-value in bpvars.$breakpoints {
41
- @if $bp-value != 0 {
60
+ // xs (0px) no media query wrapper; base classes already cover it.
61
+ @if $bp-value != 0px {
42
62
  @media (min-width: #{$bp-value}) {
43
63
  @each $name, $config in $utilities {
44
64
  @if map.get($config, responsive) == true {
45
- $prop: map.get($config, property);
65
+ $prop: map.get($config, property);
46
66
  $values: map.get($config, values);
47
67
  $prefix: map.get($config, prefix);
48
68
 
49
69
  @each $suffix, $value in $values {
50
- $class: null;
51
- @if $prefix {
52
- $class: "#{$cls-prefix}#{$prefix}-#{$suffix}";
53
- } @else {
54
- $class: "#{$cls-prefix}#{$suffix}";
55
- }
70
+ $class: if($prefix, "#{$cls-prefix}#{$prefix}-#{$suffix}", "#{$cls-prefix}#{$suffix}");
56
71
 
57
- // Escape numeric-leading breakpoint keys (e.g. "2xl" → "\32 xl")
58
- $first: string.slice("#{$bp-key}", 1, 1);
59
- $rest: string.slice("#{$bp-key}", 2);
60
- $safe-bp: null;
61
- @if $first == "0" or $first == "1" or $first == "2" or $first == "3" or
62
- $first == "4" or $first == "5" or $first == "6" or $first == "7" or
63
- $first == "8" or $first == "9" {
64
- $safe-bp: "\\3#{$first} #{$rest}";
65
- } @else {
66
- $safe-bp: "#{$bp-key}";
67
- }
72
+ // CSS identifiers cannot start with a digit.
73
+ // Escape numeric-leading breakpoint keys (e.g. "2xl" "\32 xl").
74
+ $first: string.slice("#{$bp-key}", 1, 1);
75
+ $rest: string.slice("#{$bp-key}", 2);
76
+ $safe-bp: if(
77
+ $first == "0" or $first == "1" or $first == "2" or $first == "3" or
78
+ $first == "4" or $first == "5" or $first == "6" or $first == "7" or
79
+ $first == "8" or $first == "9",
80
+ "\\3#{$first} #{$rest}",
81
+ "#{$bp-key}"
82
+ );
68
83
 
69
84
  @at-root .#{$safe-bp}\:#{$class} {
70
- @if $use-important {
71
- #{$prop}: $value !important;
72
- } @else {
73
- #{$prop}: $value;
74
- }
85
+ #{$prop}: $value $important;
75
86
  }
76
87
  }
77
88
  }
@@ -4,14 +4,18 @@
4
4
 
5
5
  // NOTE: Responsive class generation is now handled directly inside
6
6
  // generate-utilities() in _class-generator.scss (Pass 2).
7
+ //
7
8
  // This file previously contained a generate-responsive() mixin that
8
9
  // accepted @content blocks but never emitted any CSS itself, making it
9
10
  // a silent no-op. That stub has been removed to prevent confusion.
11
+ //
10
12
  // — If you need to run the responsive engine standalone (e.g. from a
11
13
  // sub-package that builds its own $utilities map independently of
12
14
  // generate-utilities), use the engine mixin directly:
15
+ //
13
16
  // @use "@mastors/core/scss/responsive/engine" as engine;
14
17
  // @include engine.run($my-utilities);
18
+ //
15
19
  // — For the standard case, simply pass your $utilities map to
16
20
  // generate-utilities() and set responsive: true on any entry that
17
21
  // needs breakpoint variants — the responsive pass runs automatically.
@@ -1,39 +1,26 @@
1
1
  // helpers/_visually-hidden.scss
2
2
  // Visually hides content while keeping it accessible to screen readers.
3
3
  // ─────────────────────────────────────────────────────────────
4
- // The canonical implementation lives in accessibility/_screen-reader.scss
5
- // (.sr-only / .not-sr-only — the modern standard class names).
6
- // This file provides the legacy aliases (.visually-hidden, .vh,
7
- // .visually-hidden-focusable) that map onto the same rules, so existing
8
- // codebases using either naming convention continue to work without
9
- // duplicating any declarations.
10
4
 
11
- @use "../accessibility/screen-reader" as *;
5
+ // Shared styles as a placeholder to avoid cross-module @extend issues
6
+ %visually-hidden-base {
7
+ position: absolute !important;
8
+ width: 1px !important;
9
+ height: 1px !important;
10
+ padding: 0 !important;
11
+ margin: -1px !important;
12
+ overflow: hidden !important;
13
+ clip: rect(0, 0, 0, 0) !important;
14
+ white-space: nowrap !important;
15
+ border: 0 !important;
16
+ }
12
17
 
13
- // Legacy aliases — extend the canonical sr-only rules
14
- // so no extra CSS is emitted; both class names share one rule block.
15
18
  .visually-hidden,
16
19
  .vh {
17
- position: absolute;
18
- width: 1px;
19
- height: 1px;
20
- padding: 0;
21
- margin: -1px;
22
- overflow: hidden;
23
- clip: rect(0, 0, 0, 0);
24
- white-space: nowrap;
25
- border-width: 0;
20
+ @extend %visually-hidden-base;
26
21
  }
27
22
 
28
23
  // Undo visually-hidden when focused (e.g. skip links)
29
24
  .visually-hidden-focusable:not(:focus):not(:focus-within) {
30
- position: absolute;
31
- width: 1px;
32
- height: 1px;
33
- padding: 0;
34
- margin: -1px;
35
- overflow: hidden;
36
- clip: rect(0, 0, 0, 0);
37
- white-space: nowrap;
38
- border-width: 0;
25
+ @extend %visually-hidden-base;
39
26
  }
@@ -9,10 +9,10 @@
9
9
  /// @param {String} $bp - breakpoint key (sm, md, lg, xl, 2xl)
10
10
  @mixin bp($bp) {
11
11
  $value: map.get(bpvars.$breakpoints, $bp);
12
- @if not $value {
12
+ @if $value == null {
13
13
  @error "bp(): unknown breakpoint '#{$bp}'. Available: #{map.keys(bpvars.$breakpoints)}";
14
14
  }
15
- @if $value == 0 {
15
+ @if $value == 0px {
16
16
  @content;
17
17
  } @else {
18
18
  @media (min-width: #{$value}) { @content; }
@@ -26,7 +26,7 @@
26
26
  /// Emit styles below a named breakpoint
27
27
  @mixin breakpoint-down($bp) {
28
28
  $value: map.get(bpvars.$breakpoints, $bp);
29
- @if not $value {
29
+ @if $value == null {
30
30
  @error "breakpoint-down(): unknown breakpoint '#{$bp}'";
31
31
  }
32
32
  @media (max-width: #{$value - 0.02px}) { @content; }
@@ -10,7 +10,7 @@
10
10
  /// @param {String} $duration - duration token key (default: "200")
11
11
  /// @param {String} $easing - easing token key (default: "in-out")
12
12
  @mixin transition($properties: (all), $duration: "200", $easing: "in-out") {
13
- $dur: tr.duration($duration);
13
+ $dur: tr.duration($duration);
14
14
  $ease: tr.easing($easing);
15
15
  $transitions: ();
16
16
  @each $prop in $properties {
@@ -8,8 +8,10 @@
8
8
 
9
9
  // Responsive engine — generates breakpoint-prefixed variants
10
10
  // of any utility that declares responsive: true.
11
+ //
11
12
  // Class pattern: .{bp}\:{utility}
12
13
  // Example output: .sm\:hidden, .md\:flex, .lg\:gap-8
14
+ //
13
15
  // Sub-packages pass their $utilities map here via @include engine.run()
14
16
  // so the engine iterates breakpoints and re-emits the classes.
15
17
 
@@ -19,11 +21,11 @@
19
21
  @if flags.$enable-responsive {
20
22
  @each $bp-key, $bp-value in bpvars.$breakpoints {
21
23
  // Skip xs — no prefix, already emitted as base classes
22
- @if $bp-value != 0 {
24
+ @if $bp-value != 0px {
23
25
  @media (min-width: #{$bp-value}) {
24
26
  @each $name, $config in $utilities {
25
27
  @if map.get($config, responsive) == true {
26
- $prop: map.get($config, property);
28
+ $prop: map.get($config, property);
27
29
  $values: map.get($config, values);
28
30
  $prefix: map.get($config, prefix);
29
31
 
@@ -39,7 +41,7 @@
39
41
  // When the breakpoint key begins with a number (e.g. "2xl"),
40
42
  // escape it using the CSS identifier-escape syntax: \3X (space after hex).
41
43
  $first: string.slice("#{$bp-key}", 1, 1);
42
- $rest: string.slice("#{$bp-key}", 2);
44
+ $rest: string.slice("#{$bp-key}", 2);
43
45
  $safe-bp: null;
44
46
  @if $first == "0" or $first == "1" or $first == "2" or $first == "3" or
45
47
  $first == "4" or $first == "5" or $first == "6" or $first == "7" or
@@ -7,6 +7,7 @@
7
7
 
8
8
  // Fluid type scale — font sizes that scale smoothly between two viewport widths.
9
9
  // Uses clamp() so no media queries are needed.
10
+ //
10
11
  // Usage in component SCSS:
11
12
  // @use "@mastors/core/api" as m;
12
13
  // font-size: m.fluid-type(1rem, 1.5rem);
@@ -16,7 +17,7 @@
16
17
  /// @param {Number} $max-size - maximum font size (rem)
17
18
  /// @param {Number} $min-vw - viewport at which min applies (px, default 320px)
18
19
  /// @param {Number} $max-vw - viewport at which max applies (px, default 1280px)
19
- @mixin apply-fluid-type($min-size, $max-size, $min-vw: 320px, $max-vw: 1280px) {
20
+ @mixin fluid-type($min-size, $max-size, $min-vw: 320px, $max-vw: 1280px) {
20
21
  font-size: mathfn.fluid($min-size, $max-size, $min-vw, $max-vw);
21
22
  }
22
23
 
@@ -30,11 +31,11 @@
30
31
  // Include with: @use "@mastors/core/scss/responsive/fluid-type" as ft;
31
32
  // @include ft.fluid-scale();
32
33
  @mixin fluid-scale {
33
- h1 { @include apply-fluid-type(2rem, 3.75rem); }
34
- h2 { @include apply-fluid-type(1.5rem, 3rem); }
35
- h3 { @include apply-fluid-type(1.25rem, 2.25rem); }
36
- h4 { @include apply-fluid-type(1.125rem, 1.875rem); }
37
- h5 { @include apply-fluid-type(1rem, 1.5rem); }
38
- h6 { @include apply-fluid-type(0.875rem, 1.25rem); }
39
- p { @include apply-fluid-type(0.875rem, 1.125rem); }
34
+ h1 { @include fluid-type(2rem, 3.75rem); }
35
+ h2 { @include fluid-type(1.5rem, 3rem); }
36
+ h3 { @include fluid-type(1.25rem, 2.25rem); }
37
+ h4 { @include fluid-type(1.125rem, 1.875rem); }
38
+ h5 { @include fluid-type(1rem, 1.5rem); }
39
+ h6 { @include fluid-type(0.875rem, 1.25rem); }
40
+ p { @include fluid-type(0.875rem, 1.125rem); }
40
41
  }
@@ -9,21 +9,21 @@
9
9
  // emitted by themes/_base-theme.scss, _light.scss, _dark.scss.
10
10
  // Downstream packages can @use this file to access semantic names.
11
11
 
12
- $color-bg: var(--mastors-bg) !default;
13
- $color-bg-subtle: var(--mastors-bg-subtle) !default;
14
- $color-surface: var(--mastors-surface) !default;
15
- $color-surface-raised: var(--mastors-surface-raised) !default;
16
- $color-surface-overlay: var(--mastors-surface-overlay) !default;
12
+ $color-bg: var(--mastors-bg) !default;
13
+ $color-bg-subtle: var(--mastors-bg-subtle) !default;
14
+ $color-surface: var(--mastors-surface) !default;
15
+ $color-surface-raised: var(--mastors-surface-raised) !default;
16
+ $color-surface-overlay: var(--mastors-surface-overlay) !default;
17
17
 
18
- $color-text: var(--mastors-text) !default;
19
- $color-text-muted: var(--mastors-text-muted) !default;
20
- $color-text-subtle: var(--mastors-text-subtle) !default;
21
- $color-text-inverse: var(--mastors-text-inverse) !default;
18
+ $color-text: var(--mastors-text) !default;
19
+ $color-text-muted: var(--mastors-text-muted) !default;
20
+ $color-text-subtle: var(--mastors-text-subtle) !default;
21
+ $color-text-inverse: var(--mastors-text-inverse) !default;
22
22
 
23
- $color-border: var(--mastors-border) !default;
24
- $color-border-strong: var(--mastors-border-strong) !default;
23
+ $color-border: var(--mastors-border) !default;
24
+ $color-border-strong: var(--mastors-border-strong) !default;
25
25
 
26
- $color-accent: var(--mastors-accent) !default;
27
- $color-accent-hover: var(--mastors-accent-hover) !default;
28
- $color-accent-subtle: var(--mastors-accent-subtle) !default;
29
- $color-accent-text: var(--mastors-accent-text) !default;
26
+ $color-accent: var(--mastors-accent) !default;
27
+ $color-accent-hover: var(--mastors-accent-hover) !default;
28
+ $color-accent-subtle: var(--mastors-accent-subtle) !default;
29
+ $color-accent-text: var(--mastors-accent-text) !default;
@@ -6,8 +6,8 @@
6
6
 
7
7
  // Semantic spacing aliases — layout-level naming on top of raw scale tokens.
8
8
 
9
- $space-inline: sp.spacing(1) !default; // 0.25rem — tight inline gap
10
- $space-element: sp.spacing(2) !default; // 0.5rem — within a component
11
- $space-component: sp.spacing(4) !default; // 1rem — between component parts
12
- $space-section: sp.spacing(16) !default; // 4rem — between page sections
13
- $space-page: sp.spacing(24) !default; // 6rem — page-level breathing room
9
+ $space-inline: sp.spacing(1) !default; // 0.25rem — tight inline gap
10
+ $space-element: sp.spacing(2) !default; // 0.5rem — within a component
11
+ $space-component: sp.spacing(4) !default; // 1rem — between component parts
12
+ $space-section: sp.spacing(16) !default; // 4rem — between page sections
13
+ $space-page: sp.spacing(24) !default; // 6rem — page-level breathing room
@@ -6,8 +6,8 @@
6
6
 
7
7
  // Semantic typography role aliases.
8
8
 
9
- $font-display: ty.font-family("sans") !default; // Hero headings
10
- $font-heading: ty.font-family("sans") !default; // h1–h6
11
- $font-body: ty.font-family("sans") !default; // p, li, td
12
- $font-mono: ty.font-family("mono") !default; // code, pre, kbd
13
- $font-ui: ty.font-family("sans") !default; // labels, buttons, nav
9
+ $font-display: ty.font-family("sans") !default; // Hero headings
10
+ $font-heading: ty.font-family("sans") !default; // h1–h6
11
+ $font-body: ty.font-family("sans") !default; // p, li, td
12
+ $font-mono: ty.font-family("mono") !default; // code, pre, kbd
13
+ $font-ui: ty.font-family("sans") !default; // labels, buttons, nav
@@ -5,21 +5,26 @@
5
5
  // The light and dark themes (_light.scss, _dark.scss) are the actual emitters.
6
6
  // This file documents the full semantic custom property contract
7
7
  // that all themes must implement, without producing any CSS output.
8
+ //
8
9
  // Semantic custom properties contract:
10
+ //
9
11
  // Surfaces
10
12
  // --mastors-bg ← page background
11
13
  // --mastors-bg-subtle ← subdued page background
12
14
  // --mastors-surface ← card / panel surface
13
15
  // --mastors-surface-raised ← elevated surface (dropdowns, tooltips)
14
16
  // --mastors-surface-overlay ← modal/overlay surface
17
+ //
15
18
  // Text
16
19
  // --mastors-text ← primary text
17
20
  // --mastors-text-muted ← secondary / supporting text
18
21
  // --mastors-text-subtle ← tertiary / placeholder text
19
22
  // --mastors-text-inverse ← text on dark/accent surfaces
23
+ //
20
24
  // Borders
21
25
  // --mastors-border ← default border
22
26
  // --mastors-border-strong ← emphasis border
27
+ //
23
28
  // Brand / Accent
24
29
  // --mastors-accent ← primary brand color
25
30
  // --mastors-accent-hover ← accent hover state
@@ -2,18 +2,17 @@
2
2
  // Dark theme CSS custom property overrides.
3
3
  // ─────────────────────────────────────────────────────────────
4
4
 
5
- @use "../tokens/color" as ct;
5
+ @use "../tokens/color" as ct;
6
+ @use "../config/settings" as cfg;
6
7
 
7
8
  // Dark theme — inverts surface/text/border semantics.
8
- // Strategy is controlled by $dark-mode-strategy (default: "class").
9
- // "class" → activated by .dark / [data-theme="dark"] on <html>
10
- // "media" → activated by prefers-color-scheme: dark
9
+ // Activated by .dark class on <html> (class mode)
10
+ // or automatically via prefers-color-scheme (media mode).
11
11
 
12
- $dark-mode-strategy: "class" !default;
12
+ $-dark-selectors: '[data-theme="dark"], .dark';
13
13
 
14
- @if $dark-mode-strategy == "class" {
15
- [data-theme="dark"],
16
- .dark {
14
+ @if cfg.config("dark-mode") == "class" {
15
+ #{$-dark-selectors} {
17
16
  --mastors-bg: #{ct.color("neutral", 950)};
18
17
  --mastors-bg-subtle: #{ct.color("neutral", 900)};
19
18
  --mastors-surface: #{ct.color("neutral", 900)};
@@ -83,18 +83,14 @@ $color-tokens: (
83
83
  "900": #164e63,
84
84
  "950": #083344,
85
85
  ),
86
- "white": #fff,
87
- "black": #000,
86
+ "white": #ffffff,
87
+ "black": #000000,
88
88
  "transparent": transparent,
89
89
  ) !default;
90
90
 
91
91
  // Accessor: color("primary", 600)
92
92
  @function color($palette, $shade: null) {
93
93
  $p: map.get($color-tokens, $palette);
94
-
95
- @if $shade {
96
- @return map.get($p, "#{$shade}");
97
- }
98
-
99
- @return $p;
94
+ @if $shade == null { @return $p; }
95
+ @return map.get($p, "#{$shade}");
100
96
  }
@@ -4,14 +4,16 @@
4
4
 
5
5
  @use "sass:map";
6
6
 
7
+ // Multi-layer shadow values must be wrapped in extra parens inside a map
8
+ // so the comma is not misread as a map-entry separator.
7
9
  $shadow-tokens: (
8
- "xs": (0 1px 2px 0 rgb(0 0 0 / 5%)),
9
- "sm": (0 1px 3px 0 rgb(0 0 0 / 10%), 0 1px 2px -1px rgb(0 0 0 / 10%)),
10
- "md": (0 4px 6px -1px rgb(0 0 0 / 10%), 0 2px 4px -2px rgb(0 0 0 / 10%)),
11
- "lg": (0 10px 15px -3px rgb(0 0 0 / 10%), 0 4px 6px -4px rgb(0 0 0 / 10%)),
12
- "xl": (0 20px 25px -5px rgb(0 0 0 / 10%), 0 8px 10px -6px rgb(0 0 0 / 10%)),
13
- "2xl": (0 25px 50px -12px rgb(0 0 0 / 25%)),
14
- "inner": (inset 0 2px 4px 0 rgb(0 0 0 / 5%)),
10
+ "xs": (0 1px 2px 0 rgb(0 0 0 / 0.05)),
11
+ "sm": (0 1px 3px 0 rgb(0 0 0 / 0.10), 0 1px 2px -1px rgb(0 0 0 / 0.10)),
12
+ "md": (0 4px 6px -1px rgb(0 0 0 / 0.10), 0 2px 4px -2px rgb(0 0 0 / 0.10)),
13
+ "lg": (0 10px 15px -3px rgb(0 0 0 / 0.10), 0 4px 6px -4px rgb(0 0 0 / 0.10)),
14
+ "xl": (0 20px 25px -5px rgb(0 0 0 / 0.10), 0 8px 10px -6px rgb(0 0 0 / 0.10)),
15
+ "2xl": (0 25px 50px -12px rgb(0 0 0 / 0.25)),
16
+ "inner": (inset 0 2px 4px 0 rgb(0 0 0 / 0.05)),
15
17
  "none": none,
16
18
  ) !default;
17
19
 
@@ -5,7 +5,8 @@
5
5
  @use "sass:map";
6
6
 
7
7
  $sizing-tokens: (
8
- "0": 0,
8
+ // Fixed scale (shares spacing scale keys)
9
+ "0": 0px,
9
10
  "px": 1px,
10
11
  "1": 0.25rem,
11
12
  "2": 0.5rem,
@@ -23,15 +24,17 @@ $sizing-tokens: (
23
24
  "72": 18rem,
24
25
  "80": 20rem,
25
26
  "96": 24rem,
27
+ // Fractions
26
28
  "1\\/2": 50%,
27
- "1\\/3": 33.3333%,
28
- "2\\/3": 66.6667%,
29
+ "1\\/3": 33.333333%,
30
+ "2\\/3": 66.666667%,
29
31
  "1\\/4": 25%,
30
32
  "3\\/4": 75%,
31
33
  "1\\/5": 20%,
32
34
  "2\\/5": 40%,
33
35
  "3\\/5": 60%,
34
36
  "4\\/5": 80%,
37
+ // Keywords
35
38
  "auto": auto,
36
39
  "full": 100%,
37
40
  "screen": 100vw,