@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
@@ -1,259 +0,0 @@
1
- #!/usr/bin/env node
2
- // scripts/generate-tokens.js
3
- // ─────────────────────────────────────────────────────────────
4
- // Reads SCSS token map files and regenerates src/tokens.ts so
5
- // the TypeScript token mirror never drifts from the SCSS source.
6
- //
7
- // Run: node scripts/generate-tokens.js
8
- // Auto: called by `node build.js` before tsc (see build.js)
9
- // ─────────────────────────────────────────────────────────────
10
-
11
- 'use strict'
12
-
13
- const fs = require('fs')
14
- const path = require('path')
15
-
16
- const TOKENS_DIR = path.join(__dirname, '..', 'scss', 'tokens')
17
- const OUT_FILE = path.join(__dirname, '..', 'src', 'tokens.ts')
18
-
19
- // ── Helpers ──────────────────────────────────────────────────
20
-
21
- /** Extract all $map-name: ( ... ) blocks from a SCSS string */
22
- function extractMaps(scss) {
23
- const maps = {}
24
- const mapRe = /\$([a-z][a-z0-9-]*)\s*:\s*\(/g
25
- let match
26
- while ((match = mapRe.exec(scss)) !== null) {
27
- const varName = match[1]
28
- let depth = 1
29
- let i = match.index + match[0].length
30
- let inner = ''
31
- while (i < scss.length && depth > 0) {
32
- if (scss[i] === '(') depth++
33
- else if (scss[i] === ')') depth--
34
- if (depth > 0) inner += scss[i]
35
- i++
36
- }
37
- maps[varName] = parseMap(inner)
38
- }
39
- return maps
40
- }
41
-
42
- /** Parse a flat SCSS map body into a JS object (string keys → string values) */
43
- function parseMap(body) {
44
- const result = {}
45
- const lines = body.split('\n')
46
- for (const raw of lines) {
47
- const line = raw.trim().replace(/,$/, '').replace(/\/\/.*$/, '').trim()
48
- if (!line || line.startsWith('(') || line.endsWith('(')) continue
49
- const colonIdx = line.indexOf(':')
50
- if (colonIdx === -1) continue
51
- const rawKey = line.slice(0, colonIdx).trim().replace(/^["']|["']$/g, '')
52
- const rawVal = line.slice(colonIdx + 1).trim()
53
- if (!rawKey || !rawVal) continue
54
- // Unescape SCSS key escapes: "0\\.5" → "0.5", "1\\/2" → "1/2"
55
- const key = rawKey.replace(/\\\\?\./g, '.').replace(/\\\\?\//g, '/')
56
- result[key] = rawVal
57
- }
58
- return result
59
- }
60
-
61
- /** Parse nested color map ($color-tokens with sub-palettes) */
62
- function extractNestedColorMap(scss) {
63
- const palettes = {}
64
- const paletteRe = /"([a-z]+)"\s*:\s*\(([\s\S]*?)\),/g
65
- let m
66
- while ((m = paletteRe.exec(scss)) !== null) {
67
- const name = m[1]
68
- const inner = m[2]
69
- if (inner.includes(':')) {
70
- palettes[name] = parseMap(inner)
71
- } else {
72
- palettes[name] = inner.trim()
73
- }
74
- }
75
- // flat top-level entries (white, black, transparent)
76
- const flatRe = /"(white|black|transparent)"\s*:\s*([^,\n]+)/g
77
- while ((m = flatRe.exec(scss)) !== null) {
78
- palettes[m[1]] = m[2].trim().replace(/,$/, '')
79
- }
80
- return palettes
81
- }
82
-
83
- // ── Read token files ──────────────────────────────────────────
84
-
85
- function readScss(filename) {
86
- return fs.readFileSync(path.join(TOKENS_DIR, filename), 'utf8')
87
- }
88
-
89
- const colorScss = readScss('_color.scss')
90
- const spacingScss = readScss('_spacing.scss')
91
- const typographyScss = readScss('_typography.scss')
92
- const shadowsScss = readScss('_shadows.scss')
93
- const radiiScss = readScss('_radii.scss')
94
- const transitionsScss = readScss('_transitions.scss')
95
- const zIndexScss = readScss('_z-index.scss')
96
- const opacityScss = readScss('_opacity.scss')
97
- const sizingScss = readScss('_sizing.scss')
98
-
99
- const colorMap = extractNestedColorMap(colorScss)
100
- const spacingMap = extractMaps(spacingScss)['spacing-tokens'] || {}
101
- const fontSizeMap = extractMaps(typographyScss)['font-size-tokens'] || {}
102
- const fontFamilyMap = extractMaps(typographyScss)['font-family-tokens'] || {}
103
- const fontWeightMap = extractMaps(typographyScss)['font-weight-tokens'] || {}
104
- const lineHeightMap = extractMaps(typographyScss)['line-height-tokens'] || {}
105
- const trackingMap = extractMaps(typographyScss)['letter-spacing-tokens'] || {}
106
- const shadowMap = extractMaps(shadowsScss)['shadow-tokens'] || {}
107
- const radiusMap = extractMaps(radiiScss)['radius-tokens'] || {}
108
- const durationMap = extractMaps(transitionsScss)['duration-tokens'] || {}
109
- const easingMap = extractMaps(transitionsScss)['easing-tokens'] || {}
110
- const zIndexMap = extractMaps(zIndexScss)['z-index-tokens'] || {}
111
- const opacityMap = extractMaps(opacityScss)['opacity-tokens'] || {}
112
- const sizingMap = extractMaps(sizingScss)['sizing-tokens'] || {}
113
-
114
- // ── Serialise to TypeScript ───────────────────────────────────
115
-
116
- function toTsObj(map, indent = 2) {
117
- const pad = ' '.repeat(indent)
118
- const entries = Object.entries(map).map(([k, v]) => {
119
- const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? k : `'${k}'`
120
- const val = typeof v === 'object'
121
- ? `{\n${toTsObj(v, indent + 2)}\n${pad}}`
122
- : `'${String(v)}'`
123
- return `${pad}${key}: ${val},`
124
- })
125
- return entries.join('\n')
126
- }
127
-
128
- // ── Generate file ─────────────────────────────────────────────
129
-
130
- const now = new Date().toISOString().slice(0, 10)
131
-
132
- const out = `\
133
- /**
134
- * @mastors/core — src/tokens.ts
135
- * AUTO-GENERATED by scripts/generate-tokens.js on ${now}.
136
- * DO NOT EDIT MANUALLY — run \`node scripts/generate-tokens.js\` to regenerate.
137
- *
138
- * Source of truth: packages/core/scss/tokens/
139
- */
140
-
141
- // ─── Color ───────────────────────────────────────────────────────────────────
142
-
143
- const colorTokens = {
144
- ${toTsObj(colorMap)}
145
- } as const
146
-
147
- // ─── Spacing ─────────────────────────────────────────────────────────────────
148
-
149
- const spacingTokens = {
150
- ${toTsObj(spacingMap)}
151
- } as const
152
-
153
- // ─── Typography ──────────────────────────────────────────────────────────────
154
-
155
- const fontSizeTokens = {
156
- ${toTsObj(fontSizeMap)}
157
- } as const
158
-
159
- const fontFamilyTokens = {
160
- ${toTsObj(fontFamilyMap)}
161
- } as const
162
-
163
- const fontWeightTokens = {
164
- ${toTsObj(fontWeightMap)}
165
- } as const
166
-
167
- const lineHeightTokens = {
168
- ${toTsObj(lineHeightMap)}
169
- } as const
170
-
171
- const letterSpacingTokens = {
172
- ${toTsObj(trackingMap)}
173
- } as const
174
-
175
- // ─── Border radius ───────────────────────────────────────────────────────────
176
-
177
- const radiusTokens = {
178
- ${toTsObj(radiusMap)}
179
- } as const
180
-
181
- // ─── Shadows ─────────────────────────────────────────────────────────────────
182
-
183
- const shadowTokens = {
184
- ${toTsObj(shadowMap)}
185
- } as const
186
-
187
- // ─── Duration & easing ───────────────────────────────────────────────────────
188
-
189
- const durationTokens = {
190
- ${toTsObj(durationMap)}
191
- } as const
192
-
193
- const easingTokens = {
194
- ${toTsObj(easingMap)}
195
- } as const
196
-
197
- // ─── Z-index ─────────────────────────────────────────────────────────────────
198
-
199
- const zIndexTokens = {
200
- ${toTsObj(zIndexMap)}
201
- } as const
202
-
203
- // ─── Opacity ─────────────────────────────────────────────────────────────────
204
-
205
- const opacityTokens = {
206
- ${toTsObj(opacityMap)}
207
- } as const
208
-
209
- // ─── Sizing ──────────────────────────────────────────────────────────────────
210
-
211
- const sizingTokens = {
212
- ${toTsObj(sizingMap)}
213
- } as const
214
-
215
- // ─── Exported tokens object ───────────────────────────────────────────────────
216
-
217
- export const tokens = {
218
- color: colorTokens,
219
- spacing: spacingTokens,
220
- fontSize: fontSizeTokens,
221
- fontFamily: fontFamilyTokens,
222
- fontWeight: fontWeightTokens,
223
- lineHeight: lineHeightTokens,
224
- letterSpacing: letterSpacingTokens,
225
- radius: radiusTokens,
226
- shadow: shadowTokens,
227
- duration: durationTokens,
228
- easing: easingTokens,
229
- zIndex: zIndexTokens,
230
- opacity: opacityTokens,
231
- sizing: sizingTokens,
232
- } as const
233
-
234
- // ─── Types ───────────────────────────────────────────────────────────────────
235
-
236
- export type Tokens = typeof tokens
237
- export type ColorPalette = keyof typeof colorTokens
238
- export type SpacingKey = keyof typeof spacingTokens
239
- export type FontSizeKey = keyof typeof fontSizeTokens
240
- export type FontFamilyKey = keyof typeof fontFamilyTokens
241
- export type FontWeightKey = keyof typeof fontWeightTokens
242
- export type LineHeightKey = keyof typeof lineHeightTokens
243
- export type LetterSpacingKey = keyof typeof letterSpacingTokens
244
- export type RadiusKey = keyof typeof radiusTokens
245
- export type ShadowKey = keyof typeof shadowTokens
246
- export type DurationKey = keyof typeof durationTokens
247
- export type EasingKey = keyof typeof easingTokens
248
- export type ZIndexKey = keyof typeof zIndexTokens
249
- export type OpacityKey = keyof typeof opacityTokens
250
- export type SizingKey = keyof typeof sizingTokens
251
-
252
- export type ColorShade<P extends ColorPalette> =
253
- typeof colorTokens[P] extends Record<string, string>
254
- ? keyof typeof colorTokens[P]
255
- : never
256
- `
257
-
258
- fs.writeFileSync(OUT_FILE, out, 'utf8')
259
- console.log('[generate-tokens] Written → ' + path.relative(process.cwd(), OUT_FILE))
@@ -1,52 +0,0 @@
1
- // accessibility/_print.scss
2
- // Print-specific utilities and media query helpers.
3
- // ─────────────────────────────────────────────────────────────
4
-
5
- // Hide from print
6
- .print\:hidden {
7
- @media print { display: none !important; }
8
- }
9
-
10
- // Show only in print
11
- .screen\:hidden {
12
- @media not print { display: none !important; }
13
- }
14
-
15
- @media print {
16
- // Avoid page breaks inside these elements by default
17
- .print\:break-inside-avoid {
18
- break-inside: avoid;
19
- page-break-inside: avoid;
20
- }
21
-
22
- // Force page break before
23
- .print\:break-before {
24
- break-before: page;
25
- page-break-before: always;
26
- }
27
-
28
- // Force page break after
29
- .print\:break-after {
30
- break-after: page;
31
- page-break-after: always;
32
- }
33
-
34
- // Print-safe color override (ensure text is black on white)
35
- .print\:text-black { color: #000 !important; }
36
- .print\:bg-white { background: #fff !important; }
37
- .print\:border-none { border: none !important; }
38
- .print\:shadow-none { box-shadow: none !important; }
39
-
40
- // Expand truncated links for print readers
41
- a[href]::after {
42
- content: " (" attr(href) ")";
43
- font-size: 0.75em;
44
- color: #555;
45
- }
46
-
47
- // Don't expand hash or JS links
48
- a[href^="#"]::after,
49
- a[href^="javascript:"]::after {
50
- content: "";
51
- }
52
- }
@@ -1,12 +0,0 @@
1
- // config/_index.scss
2
- // Public config surface — forward both partials so downstream
3
- // consumers can reach flags and settings via the public API.
4
- // ─────────────────────────────────────────────────────────────
5
- // Usage (downstream):
6
- // @use "@mastors/core/api" as m;
7
- // $dark: m.$enable-themes; // from flags
8
- // $pfx: m.config("prefix"); // from settings
9
- // ─────────────────────────────────────────────────────────────
10
-
11
- @forward "settings";
12
- @forward "flags";
@@ -1,49 +0,0 @@
1
- // functions/_vars.scss
2
- // vars() — reference any design token as a CSS custom property by name.
3
- // ─────────────────────────────────────────────────────────────
4
- // All Mastors tokens are emitted as --mastors-{name} custom properties
5
- // by the token layer. This function wraps that convention so downstream
6
- // consumers never have to hard-code the --mastors- prefix themselves,
7
- // and so token references remain refactor-safe if the prefix ever changes.
8
- // Usage:
9
- // color: vars(accent); → var(--mastors-accent)
10
- // background-color: vars(surface-raised); → var(--mastors-surface-raised)
11
- // box-shadow: vars(shadow-lg); → var(--mastors-shadow-lg)
12
- // With a CSS fallback (passed through verbatim):
13
- // color: vars(accent, #3b82f6); → var(--mastors-accent, #3b82f6)
14
- // gap: vars(spacing-4, 1rem); → var(--mastors-spacing-4, 1rem)
15
- // Composing with other functions is fine — Sass evaluates eagerly:
16
- // transition: opacity vars(duration-200) vars(ease-out);
17
- // ─────────────────────────────────────────────────────────────
18
-
19
- @use "sass:meta";
20
- @use "sass:string";
21
- @use "../variables/global" as g;
22
-
23
- // Internal: the custom-property namespace prefix.
24
- // Mirrors the --mastors- prefix used by the token emitter.
25
- // Kept as a private variable so it only needs to change here.
26
- $-namespace: "mastors" !default;
27
-
28
- /// Return a CSS var() expression for a design token by name.
29
- ///
30
- /// @param {String} $token - token name, without the --mastors- prefix
31
- /// @param {*} $fallback - optional CSS fallback value (default: null)
32
- /// @return {String} var(--mastors-{$token}) or var(--mastors-{$token}, {$fallback})
33
- ///
34
- /// @example scss
35
- /// .btn { color: vars(accent); }
36
- /// // → .btn { color: var(--mastors-accent); }
37
- ///
38
- /// @example scss
39
- /// .card { box-shadow: vars(shadow-md, 0 2px 8px rgb(0 0 0 / .1)); }
40
- /// // → .card { box-shadow: var(--mastors-shadow-md, 0 2px 8px rgb(0 0 0 / .1)); }
41
- @function vars($token, $fallback: null) {
42
- $prop: "--#{$-namespace}-#{$token}";
43
-
44
- @if not $fallback {
45
- @return var(#{$prop});
46
- } @else {
47
- @return var(#{$prop}, #{$fallback});
48
- }
49
- }
@@ -1,125 +0,0 @@
1
- // utilities/_animation.scss
2
- // Transition and animation utility classes.
3
- // ─────────────────────────────────────────────────────────────
4
-
5
- @use "../tokens/transitions" as tr;
6
- @use "../generators/class-generator" as gen;
7
-
8
- // ── Transition property ───────────────────────────────────────────────────────
9
-
10
- .transition-none { transition-property: none; }
11
- .transition-all { transition-property: all; }
12
- .transition { transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; }
13
- .transition-colors { transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; }
14
- .transition-opacity { transition-property: opacity; }
15
- .transition-shadow { transition-property: box-shadow; }
16
- .transition-transform { transition-property: transform; }
17
-
18
- // ── Transition duration ───────────────────────────────────────────────────────
19
-
20
- @each $key, $val in tr.$duration-tokens {
21
- .duration-#{$key} { transition-duration: $val; }
22
- }
23
-
24
- // ── Transition timing function ────────────────────────────────────────────────
25
-
26
- @each $key, $val in tr.$easing-tokens {
27
- .ease-#{$key} { transition-timing-function: $val; }
28
- }
29
-
30
- // ── Transition delay ──────────────────────────────────────────────────────────
31
-
32
- @each $key, $val in tr.$duration-tokens {
33
- .delay-#{$key} { transition-delay: $val; }
34
- }
35
-
36
- // ── Animation ────────────────────────────────────────────────────────────────
37
-
38
- .animate-none { animation: none; }
39
- .animate-spin { animation: mastors-spin 1s linear infinite; }
40
- .animate-ping { animation: mastors-ping 1s cubic-bezier(0, 0, 0.2, 1) infinite; }
41
- .animate-pulse { animation: mastors-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
42
- .animate-bounce { animation: mastors-bounce 1s infinite; }
43
- .animate-fade-in { animation: mastors-fade-in 0.3s ease-out both; }
44
- .animate-fade-out{ animation: mastors-fade-out 0.3s ease-in both; }
45
- .animate-slide-up { animation: mastors-slide-up 0.3s ease-out both; }
46
- .animate-slide-down { animation: mastors-slide-down 0.3s ease-out both; }
47
- .animate-scale-in { animation: mastors-scale-in 0.2s ease-out both; }
48
-
49
- // ── Keyframe definitions ─────────────────────────────────────────────────────
50
-
51
- @keyframes mastors-spin {
52
- from { transform: rotate(0deg); }
53
- to { transform: rotate(360deg); }
54
- }
55
-
56
- @keyframes mastors-ping {
57
- 75%, 100% { transform: scale(2); opacity: 0%; }
58
- }
59
-
60
- @keyframes mastors-pulse {
61
- 0%, 100% { opacity: 100%; }
62
- 50% { opacity: 50%; }
63
- }
64
-
65
- @keyframes mastors-bounce {
66
- 0%, 100% { transform: translateY(-25%); animation-timing-function: cubic-bezier(0.8, 0, 1, 1); }
67
- 50% { transform: translateY(0); animation-timing-function: cubic-bezier(0, 0, 0.2, 1); }
68
- }
69
-
70
- @keyframes mastors-fade-in {
71
- from { opacity: 0%; }
72
- to { opacity: 100%; }
73
- }
74
-
75
- @keyframes mastors-fade-out {
76
- from { opacity: 100%; }
77
- to { opacity: 0%; }
78
- }
79
-
80
- @keyframes mastors-slide-up {
81
- from { opacity: 0%; transform: translateY(0.5rem); }
82
- to { opacity: 100%; transform: translateY(0); }
83
- }
84
-
85
- @keyframes mastors-slide-down {
86
- from { opacity: 0%; transform: translateY(-0.5rem); }
87
- to { opacity: 100%; transform: translateY(0); }
88
- }
89
-
90
- @keyframes mastors-scale-in {
91
- from { opacity: 0%; transform: scale(0.95); }
92
- to { opacity: 100%; transform: scale(1); }
93
- }
94
-
95
- // ── Animation control utilities ───────────────────────────────────────────────
96
-
97
- @include gen.generate-utilities((
98
- "animation-fill-mode": (
99
- property: animation-fill-mode,
100
- prefix: "fill",
101
- values: (
102
- "none": none,
103
- "forwards": forwards,
104
- "backwards": backwards,
105
- "both": both,
106
- ),
107
- ),
108
- "animation-play-state": (
109
- property: animation-play-state,
110
- prefix: "animation",
111
- values: (
112
- "running": running,
113
- "paused": paused,
114
- ),
115
- ),
116
- "animation-iteration": (
117
- property: animation-iteration-count,
118
- prefix: "animate-repeat",
119
- values: (
120
- "0": 0,
121
- "1": 1,
122
- "infinite": infinite,
123
- ),
124
- ),
125
- ));
@@ -1,156 +0,0 @@
1
- // utilities/_interaction.scss
2
- // Interaction and user-experience utilities:
3
- // user-select, resize, scroll-behavior, scroll-snap, touch-action,
4
- // and state-variant prefixes (hover:, focus:, active:, disabled:).
5
- // ─────────────────────────────────────────────────────────────
6
-
7
- @use "../generators/class-generator" as gen;
8
-
9
- // ── User select ───────────────────────────────────────────────────────────────
10
-
11
- @include gen.generate-utilities((
12
- "select": (
13
- property: user-select,
14
- prefix: "select",
15
- values: (
16
- "none": none,
17
- "text": text,
18
- "all": all,
19
- "auto": auto,
20
- ),
21
- ),
22
- ));
23
-
24
- // ── Resize ────────────────────────────────────────────────────────────────────
25
-
26
- @include gen.generate-utilities((
27
- "resize": (
28
- property: resize,
29
- prefix: "resize",
30
- values: (
31
- "none": none,
32
- "x": horizontal,
33
- "y": vertical,
34
- "": both, // .resize
35
- ),
36
- ),
37
- ));
38
-
39
- // ── Scroll behavior ───────────────────────────────────────────────────────────
40
-
41
- .scroll-auto { scroll-behavior: auto; }
42
- .scroll-smooth { scroll-behavior: smooth; }
43
-
44
- // ── Scroll snap ───────────────────────────────────────────────────────────────
45
-
46
- // Container
47
- @include gen.generate-utilities((
48
- "snap-type": (
49
- property: scroll-snap-type,
50
- prefix: "snap",
51
- values: (
52
- "none": none,
53
- "x": x var(--mastors-snap-strictness, mandatory),
54
- "y": y var(--mastors-snap-strictness, mandatory),
55
- "both": both var(--mastors-snap-strictness, mandatory),
56
- "mandatory": x mandatory,
57
- "proximity": x proximity,
58
- ),
59
- ),
60
- ));
61
-
62
- // Item
63
- @include gen.generate-utilities((
64
- "snap-align": (
65
- property: scroll-snap-align,
66
- prefix: "snap",
67
- values: (
68
- "start": start,
69
- "end": end,
70
- "center": center,
71
- "align-none": none,
72
- ),
73
- ),
74
- ));
75
-
76
- .snap-stop-always { scroll-snap-stop: always; }
77
- .snap-stop-normal { scroll-snap-stop: normal; }
78
-
79
- // ── Scroll margin/padding ─────────────────────────────────────────────────────
80
-
81
- $-scroll-values: ("0": 0px, "4": 1rem, "8": 2rem, "16": 4rem);
82
-
83
- @each $key, $val in $-scroll-values {
84
- .scroll-m-#{$key} { scroll-margin: $val; }
85
- .scroll-mt-#{$key} { scroll-margin-top: $val; }
86
- .scroll-mb-#{$key} { scroll-margin-bottom: $val; }
87
- .scroll-p-#{$key} { scroll-padding: $val; }
88
- .scroll-pt-#{$key} { scroll-padding-top: $val; }
89
- .scroll-pb-#{$key} { scroll-padding-bottom: $val; }
90
- }
91
-
92
- // ── Touch action ──────────────────────────────────────────────────────────────
93
-
94
- @include gen.generate-utilities((
95
- "touch": (
96
- property: touch-action,
97
- prefix: "touch",
98
- values: (
99
- "auto": auto,
100
- "none": none,
101
- "pan-x": pan-x,
102
- "pan-y": pan-y,
103
- "pan-left": pan-left,
104
- "pan-right": pan-right,
105
- "pan-up": pan-up,
106
- "pan-down": pan-down,
107
- "pinch-zoom": pinch-zoom,
108
- "manipulation": manipulation,
109
- ),
110
- ),
111
- ));
112
-
113
- // ── State-variant pseudo-class utilities ─────────────────────────────────────
114
- // Hover, focus, active, disabled, and group-hover variants.
115
- // Pattern: .hover\:{utility}:hover { ... }
116
- // These are the most commonly used interactive overrides. For full
117
- // state-variant coverage, use a PostCSS/Vite plugin or the responsive engine.
118
-
119
- // Hover: opacity
120
- @each $key, $val in ("0": 0, "50": 0.5, "75": 0.75, "100": 1) {
121
- .hover\:opacity-#{$key}:hover { opacity: $val; }
122
- }
123
-
124
- // Hover: background accent
125
- .hover\:bg-accent:hover { background-color: var(--mastors-accent-hover); }
126
- .hover\:text-accent:hover { color: var(--mastors-accent-hover); }
127
- .hover\:underline:hover { text-decoration-line: underline; }
128
- .hover\:no-underline:hover { text-decoration-line: none; }
129
- .hover\:shadow-lg:hover { box-shadow: var(--mastors-shadow-lg); }
130
- .hover\:scale-105:hover { transform: scale(1.05); }
131
- .hover\:scale-110:hover { transform: scale(1.1); }
132
- .hover\:-translate-y-1:hover { transform: translateY(-0.25rem); }
133
-
134
- // Focus-visible: ring utilities
135
- .focus\:ring:focus-visible {
136
- outline: 2px solid var(--mastors-accent);
137
- outline-offset: 2px;
138
- }
139
-
140
- .focus\:ring-2:focus-visible {
141
- outline: 2px solid var(--mastors-accent);
142
- outline-offset: 2px;
143
- }
144
-
145
- .focus\:ring-offset-2:focus-visible {
146
- outline-offset: 4px;
147
- }
148
-
149
- .focus\:ring-none:focus-visible {
150
- outline: none;
151
- }
152
-
153
- // Disabled: dim + block interaction
154
- .disabled\:opacity-50:disabled { opacity: 50%; }
155
- .disabled\:cursor-not-allowed:disabled { cursor: not-allowed; }
156
- .disabled\:pointer-events-none:disabled { pointer-events: none; }