@mezzanine-ui/system 0.16.0 → 1.0.0-canary.1

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 (51) hide show
  1. package/_system.scss +46 -7
  2. package/effect/_effect.scss +66 -0
  3. package/effect/_index.scss +1 -0
  4. package/effect/constants.d.ts +1 -0
  5. package/effect/constants.js +3 -0
  6. package/effect/index.d.ts +2 -0
  7. package/effect/index.js +1 -0
  8. package/effect/typings.d.ts +32 -0
  9. package/package.json +1 -1
  10. package/palette/_index.scss +3 -1
  11. package/palette/_palette.scss +3 -203
  12. package/palette/_primitives.scss +267 -0
  13. package/palette/_semantic.scss +537 -0
  14. package/palette/constants.d.ts +1 -1
  15. package/palette/constants.js +2 -2
  16. package/palette/index.js +1 -1
  17. package/palette/typings.d.ts +86 -0
  18. package/radius/_index.scss +1 -0
  19. package/radius/_radius.scss +71 -0
  20. package/radius/constants.d.ts +1 -0
  21. package/radius/constants.js +3 -0
  22. package/radius/index.d.ts +3 -0
  23. package/radius/index.js +2 -0
  24. package/radius/radius.d.ts +18 -0
  25. package/radius/radius.js +32 -0
  26. package/size/_size.scss +6 -1
  27. package/size/size.d.ts +2 -0
  28. package/spacing/_index.scss +3 -1
  29. package/spacing/_primitives.scss +131 -0
  30. package/spacing/_semantic.scss +541 -0
  31. package/spacing/_spacing.scss +2 -18
  32. package/spacing/constants.d.ts +1 -0
  33. package/spacing/constants.js +2 -1
  34. package/spacing/index.js +1 -1
  35. package/spacing/toSpacingCssVar.d.ts +1 -0
  36. package/spacing/toSpacingCssVar.js +1 -0
  37. package/spacing/typings.d.ts +81 -0
  38. package/typography/SF-Mono/SF-Mono-Medium.otf +0 -0
  39. package/typography/SF-Mono/SF-Mono-Regular.otf +0 -0
  40. package/typography/SF-Mono/SF-Mono-Semibold.otf +0 -0
  41. package/typography/_index.scss +4 -1
  42. package/typography/_primitives.scss +318 -0
  43. package/typography/_semantic.scss +237 -0
  44. package/typography/_sf-mono.scss +33 -0
  45. package/typography/_typography.scss +4 -248
  46. package/typography/_utils.scss +4 -0
  47. package/typography/index.js +1 -0
  48. package/typography/typings.d.ts +9 -1
  49. package/typography/typings.js +4 -0
  50. package/palette/_constants.scss +0 -267
  51. package/palette/_utils.scss +0 -47
package/_system.scss CHANGED
@@ -3,13 +3,19 @@
3
3
  @use './spacing';
4
4
  @use './typography';
5
5
  @use './z-index';
6
+ @use './radius';
7
+ @use './effect';
6
8
  @use './palette' as system-palette;
7
9
 
8
- @mixin common-variables($options: ()) {
10
+ @mixin common-variables($mode: default, $options: ()) {
9
11
  $motion: map.get($options, motion);
10
12
  $spacing: map.get($options, spacing);
11
13
  $typography: map.get($options, typography);
14
+ $typography-primitives: map.get($options, typography-primitives);
12
15
  $z-index: map.get($options, z-index);
16
+ $radius: map.get($options, radius);
17
+
18
+ @include effect.variables();
13
19
 
14
20
  @if not $motion {
15
21
  @include motion.variables();
@@ -18,15 +24,41 @@
18
24
  }
19
25
 
20
26
  @if not $spacing {
21
- @include spacing.variables();
27
+ @include spacing.semantic-variables($mode);
28
+ } @else {
29
+ @include spacing.semantic-variables($mode, $spacing);
30
+ }
31
+
32
+ @if not $typography-primitives {
33
+ @include typography.primitive-variables();
34
+ } @else {
35
+ $font-families: map.get($typography-primitives, font-families) or ();
36
+ $font-weights: map.get($typography-primitives, font-weights) or ();
37
+ $font-sizes: map.get($typography-primitives, font-sizes) or ();
38
+ $line-heights: map.get($typography-primitives, line-heights) or ();
39
+ $letter-spacings: map.get($typography-primitives, letter-spacings) or ();
40
+
41
+ @include typography.primitive-variables(
42
+ $font-families,
43
+ $font-weights,
44
+ $font-sizes,
45
+ $line-heights,
46
+ $letter-spacings
47
+ );
48
+ }
49
+
50
+ @if not $typography-primitives {
51
+ font-family: typography.primitive-get-font-family-list();
22
52
  } @else {
23
- @include spacing.variables($spacing);
53
+ $font-families: map.get($typography-primitives, font-families) or ();
54
+
55
+ font-family: typography.primitive-get-font-family-list($font-families);
24
56
  }
25
57
 
26
58
  @if not $typography {
27
- @include typography.variables();
59
+ @include typography.semantic-variables();
28
60
  } @else {
29
- @include typography.variables($typography);
61
+ @include typography.semantic-variables($typography);
30
62
  }
31
63
 
32
64
  @if not $z-index {
@@ -34,8 +66,15 @@
34
66
  } @else {
35
67
  @include z-index.variables($z-index);
36
68
  }
69
+
70
+ @if not $radius {
71
+ @include radius.variables();
72
+ } @else {
73
+ @include radius.variables($radius);
74
+ }
37
75
  }
38
76
 
39
- @mixin palette($mode: 'light', $palette: ()) {
40
- @include system-palette.variables($mode, $palette);
77
+ @mixin colors($mode: 'light', $semantic: (), $primitives: ()) {
78
+ @include system-palette.primitive-variables($primitives);
79
+ @include system-palette.semantic-variables($mode, $semantic);
41
80
  }
@@ -0,0 +1,66 @@
1
+ @use 'sass:map';
2
+ @use 'sass:string';
3
+ @use '../palette/semantic' as semantic;
4
+
5
+ $prefix: mzn-effect;
6
+
7
+ // 定義所有效果類型
8
+ $effect-contexts: (shadow, focus);
9
+
10
+ $effect-values: (
11
+ shadow: (
12
+ none: (
13
+ value: 0 0 0 0 rgb(0 0 0 / 0%),
14
+ ),
15
+ raised: (
16
+ value: 0 0 6px 0 semantic.variable(shadow, dark),
17
+ ),
18
+ floating: (
19
+ value: 0 0 16px 0 semantic.variable(shadow, dark),
20
+ ),
21
+ modal: (
22
+ value: string.unquote('0 9px 28px 8px #{semantic.variable(shadow, dark-faint)}, 0 20px 24px -4px #{semantic.variable(shadow, dark-light)}, 0 3px 6px -4px #{semantic.variable(shadow, dark-ghost)}'),
23
+ ),
24
+ table-sticky: (
25
+ value: 1px 0 4px 0 semantic.variable(shadow, dark),
26
+ ),
27
+ slider-handle: (
28
+ value: 0 2px 3px 0 semantic.variable(shadow, brand),
29
+ ),
30
+ inner-top-and-bottom: (
31
+ value: string.unquote('0 1px 1px 0 #{semantic.variable(shadow, light-faint)} inset, 0 -1px 1px 0 #{semantic.variable(shadow, light-faint)} inset, 0 0 12px 0 #{semantic.variable(shadow, dark)}'),
32
+ ),
33
+ ),
34
+ focus: (
35
+ primary: (
36
+ value: string.unquote('0 0 0 1px #{semantic.variable(background, base)}, 0 0 0 2px #{semantic.variable(background, brand)}'),
37
+ ),
38
+ error: (
39
+ value: string.unquote('0 0 0 1px #{semantic.variable(background, base)}, 0 0 0 2px #{semantic.variable(background, error)}'),
40
+ ),
41
+ ),
42
+ );
43
+
44
+ /**
45
+ * 取得 effect 變數名稱
46
+ * @param {string} $context - 效果情境 (shadow, focus)
47
+ * @param {string} $type - 效果類型
48
+ * @example
49
+ * variable(shadow, raised) => var(--mzn-effect-shadow-raised)
50
+ */
51
+ @function variable($context, $type) {
52
+ @return var(--#{$prefix}-#{$context}-#{$type});
53
+ }
54
+
55
+ /**
56
+ * 定義 CSS 變數
57
+ * @example
58
+ * @include variables();
59
+ */
60
+ @mixin variables() {
61
+ @each $context, $types in $effect-values {
62
+ @each $type, $config in $types {
63
+ --#{$prefix}-#{$context}-#{$type}: #{map.get($config, value)};
64
+ }
65
+ }
66
+ }
@@ -0,0 +1 @@
1
+ @forward './effect';
@@ -0,0 +1 @@
1
+ export declare const effectPrefix = "mzn-effect";
@@ -0,0 +1,3 @@
1
+ const effectPrefix = 'mzn-effect';
2
+
3
+ export { effectPrefix };
@@ -0,0 +1,2 @@
1
+ export * from './constants';
2
+ export * from './typings';
@@ -0,0 +1 @@
1
+ export { effectPrefix } from './constants.js';
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Shadow types - 陰影類型
3
+ */
4
+ export type ShadowType = 'none' | 'raised' | 'floating' | 'modal' | 'table-sticky' | 'slider-handle' | 'inner-top-and-bottom';
5
+ /**
6
+ * Focus types - 聚焦類型
7
+ */
8
+ export type FocusType = 'primary' | 'error';
9
+ /**
10
+ * Effect contexts - 效果情境
11
+ */
12
+ export type EffectContext = 'shadow' | 'focus';
13
+ /**
14
+ * Effect type mapping
15
+ */
16
+ export type EffectTypeMap = {
17
+ shadow: ShadowType;
18
+ focus: FocusType;
19
+ };
20
+ /**
21
+ * 取得特定 context 的 type
22
+ */
23
+ export type EffectTypeFor<C extends EffectContext> = EffectTypeMap[C];
24
+ /**
25
+ * Effect 完整型別 - context + type 組合
26
+ * @example
27
+ * const effect: Effect = 'shadow-raised';
28
+ * const effect2: Effect = 'focus-primary';
29
+ */
30
+ export type Effect = {
31
+ [C in EffectContext]: `${C}-${EffectTypeFor<C>}`;
32
+ }[EffectContext];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mezzanine-ui/system",
3
- "version": "0.16.0",
3
+ "version": "1.0.0-canary.1",
4
4
  "description": "Design System for mezzanine-ui",
5
5
  "author": "Mezzanine",
6
6
  "repository": {
@@ -1 +1,3 @@
1
- @forward './palette';
1
+ @forward './palette'; // @deprecated
2
+ @forward './primitives' as primitive-*;
3
+ @forward './semantic' as semantic-*;
@@ -1,206 +1,6 @@
1
- @use 'sass:meta';
2
- @use '../utils/list';
3
- @use '../utils/map';
4
-
5
- $prefix: mzn-color;
6
-
7
- @function _get-main-related-color-names($color) {
8
- @return ($color, #{$color}-light, #{$color}-dark, #{$color}-hover-bg, #{$color}-active-bg, on-#{$color});
9
- }
10
-
11
- @function _get-main-color-transformed-palette($color) {
12
- $rgba: meta.get-function('rgba');
13
-
14
- @return (
15
- #{$color}-hover-bg: (
16
- from: #{$color}-light,
17
- transformer: $rgba,
18
- args: (
19
- 0.15,
20
- )
21
- ),
22
- #{$color}-active-bg: (
23
- from: $color,
24
- transformer: $rgba,
25
- args: (
26
- 0.2,
27
- )
28
- )
29
- );
30
- }
31
-
32
- @function _transform-color-from-palette($palette, $config) {
33
- $transformer: map.get($config, transformer);
34
- $from: map.get($palette, map.get($config, from));
35
- $args: map.get($config, args);
36
-
37
- @return meta.call($transformer, $from, $args...);
38
- }
39
-
40
- $main-color-names: (primary, secondary, error, warning, success);
41
- $color-names: list.flat-push(
42
- list.flat-map($main-color-names, meta.get-function('_get-main-related-color-names')),
43
- (
44
- text-primary,
45
- text-secondary,
46
- text-disabled,
47
- action-active,
48
- action-inactive,
49
- action-disabled,
50
- action-disabled-bg,
51
- bg,
52
- surface,
53
- border,
54
- divider,
55
- overlay-bg,
56
- overlay-surface-bg
57
- )
58
- );
59
-
60
- $default-semantic-palette: map.assign(
61
- (
62
- error: #db2b1d,
63
- error-light: #f75142,
64
- error-dark: #c00f03,
65
- on-error: #fff,
66
- ),
67
- _get-main-color-transformed-palette(error),
68
- (
69
- warning: #f7ac38,
70
- warning-light: #fdd948,
71
- warning-dark: #f1842b,
72
- on-warning: #fff,
73
- ),
74
- _get-main-color-transformed-palette(warning),
75
- (
76
- success: #2e8d36,
77
- success-light: #42ae4a,
78
- success-dark: #0c5d19,
79
- on-success: #fff,
80
- ),
81
- _get-main-color-transformed-palette(success)
82
- );
83
-
84
- $default-light-palette: map.assign(
85
- (
86
- primary: #465bc7,
87
- primary-light: #778de8,
88
- primary-dark: #2d2d9e,
89
- on-primary: #fff,
90
- ),
91
- _get-main-color-transformed-palette(primary),
92
- (
93
- secondary: #383838,
94
- secondary-light: #6a6a6a,
95
- secondary-dark: #161616,
96
- on-secondary: #fff,
97
- ),
98
- _get-main-color-transformed-palette(secondary),
99
- $default-semantic-palette,
100
- (
101
- text-primary: #161616,
102
- text-secondary: #8f8f8f,
103
- text-disabled: #bcbcbc,
104
- action-active: #161616,
105
- action-inactive: #8f8f8f,
106
- action-disabled: #bcbcbc,
107
- action-disabled-bg: #e5e5e5,
108
- bg: #f4f4f4,
109
- surface: #fff,
110
- border: #d9d9d9,
111
- divider: #f2f2f2,
112
- overlay-bg: (
113
- from: action-active,
114
- transformer: meta.get-function('rgba'),
115
- args: (
116
- 0.5,
117
- ),
118
- ),
119
- overlay-surface-bg: (
120
- from: surface,
121
- transformer: meta.get-function('rgba'),
122
- args: (
123
- 0.9,
124
- ),
125
- ),
126
- )
127
- );
128
-
129
- $default-dark-palette: map.assign(
130
- (
131
- primary: #667cd8,
132
- primary-light: #92a7ff,
133
- primary-dark: #3440b2,
134
- on-primary: #fff,
135
- ),
136
- _get-main-color-transformed-palette(primary),
137
- (
138
- secondary: #6a6a6a,
139
- secondary-light: #b2b2b2,
140
- secondary-dark: #383838,
141
- on-secondary: #fff,
142
- ),
143
- _get-main-color-transformed-palette(secondary),
144
- $default-semantic-palette,
145
- (
146
- text-primary: #fff,
147
- text-secondary: #a8a8a8,
148
- text-disabled: #595959,
149
- action-active: #fff,
150
- action-inactive: #a8a8a8,
151
- action-disabled: #595959,
152
- action-disabled-bg: #393939,
153
- bg: #161616,
154
- surface: #242424,
155
- border: #7c7c7c,
156
- divider: #494949,
157
- overlay-bg: (
158
- from: action-active,
159
- transformer: meta.get-function('rgba'),
160
- args: (
161
- 0.5,
162
- ),
163
- ),
164
- overlay-surface-bg: (
165
- from: surface,
166
- transformer: meta.get-function('rgba'),
167
- args: (
168
- 0.9,
169
- ),
170
- ),
171
- )
172
- );
173
-
174
- $default-palette: (
175
- light: $default-light-palette,
176
- dark: $default-dark-palette,
177
- );
178
-
179
- @function is-color-name-valid($color-name) {
180
- @return list.includes($color-names, $color-name);
181
- }
182
-
183
- @mixin variables($mode, $palette: ()) {
184
- $palette: map.deep-merge($default-palette, $palette);
185
- $palette: map.get($palette, $mode);
186
-
187
- @each $color-name, $color in $palette {
188
- @if not is-color-name-valid($color-name) {
189
- @error 'Invalid color name #{$color-name}. Please choose one of #{$color-names}';
190
- }
191
-
192
- @if meta.type-of($color) != 'color' {
193
- $color: _transform-color-from-palette($palette, $color);
194
- }
195
-
196
- --#{$prefix}-#{$color-name}: #{$color};
197
- }
198
- }
199
-
1
+ /* @deprecated */
200
2
  @function color($color-name) {
201
- @if not is-color-name-valid($color-name) {
202
- @error 'Invalid color name #{$color-name}. Please choose one of #{$color-names}';
203
- }
3
+ @warn "This 'color' function is deprecated. Please use 'palette.semantic-variable' instead.";
204
4
 
205
- @return var(--#{$prefix}-#{$color-name});
5
+ @return 'none';
206
6
  }
@@ -0,0 +1,267 @@
1
+ @use 'sass:meta';
2
+ @use '../utils/list';
3
+ @use '../utils/map';
4
+
5
+ // 定義所有可用的級距
6
+ $primitive-scales: (25, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950);
7
+
8
+ // 定義所有可用的分類
9
+ $primitive-categories: (
10
+ brand,
11
+ red,
12
+ yellow,
13
+ green,
14
+ blue,
15
+ gray,
16
+ white-base,
17
+ black-base,
18
+ brand-base
19
+ );
20
+
21
+ // CSS 變數前綴
22
+ $primitive-prefix: mzn-color-primitive;
23
+
24
+ // 預設色票定義
25
+ $default-primitives: (
26
+ // 品牌色 - 數字級距: 25, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950
27
+ brand: (
28
+ 25: #FAFAFF,
29
+ 50: #F2F4FE,
30
+ 100: #E2E6FD,
31
+ 200: #C2CCFA,
32
+ 300: #9DAAF5,
33
+ 400: #7689EF,
34
+ 500: #5D74E9,
35
+ 600: #5265E1,
36
+ 700: #4353D6,
37
+ 800: #3340C2,
38
+ 900: #24318F,
39
+ 950: #1A2558,
40
+ ),
41
+ // 錯誤色 - 數字級距: 25, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950
42
+ red: (
43
+ 25: #FFFAFA,
44
+ 50: #FFF1F1,
45
+ 100: #FDDDDD,
46
+ 200: #FAB6B9,
47
+ 300: #F88B91,
48
+ 400: #F45D65,
49
+ 500: #F03740,
50
+ 600: #D03335,
51
+ 700: #B22B2D,
52
+ 800: #911F22,
53
+ 900: #6D1518,
54
+ 950: #4B0B0B,
55
+ ),
56
+ // 警告色 - 數字級距: 25, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950
57
+ yellow: (
58
+ 25: #FFFCF5,
59
+ 50: #FFFAEB,
60
+ 100: #FEF0C7,
61
+ 200: #FEDF89,
62
+ 300: #FEC84B,
63
+ 400: #FEB022,
64
+ 500: #F7900A,
65
+ 600: #DC6803,
66
+ 700: #B54708,
67
+ 800: #93370D,
68
+ 900: #7A2E0E,
69
+ 950: #4E1D09,
70
+ ),
71
+ // 成功色 - 數字級距: 25, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950
72
+ green: (
73
+ 25: #F3FCF7,
74
+ 50: #E5F9EE,
75
+ 100: #C5F0D8,
76
+ 200: #94DFB8,
77
+ 300: #57C78F,
78
+ 400: #2BB26D,
79
+ 500: #139F62,
80
+ 600: #11985A,
81
+ 700: #0E754D,
82
+ 800: #0A5C41,
83
+ 900: #064130,
84
+ 950: #042B1B,
85
+ ),
86
+ // 一般 - 數字級距: 25, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950
87
+ blue: (
88
+ 25: #F5FAFF,
89
+ 50: #EFF8FF,
90
+ 100: #D1E9FF,
91
+ 200: #B2DDFF,
92
+ 300: #84CAFF,
93
+ 400: #53B1FD,
94
+ 500: #2E90FA,
95
+ 600: #1570EF,
96
+ 700: #175CD3,
97
+ 800: #1849A9,
98
+ 900: #194185,
99
+ 950: #102A56,
100
+ ),
101
+ // 中性灰階 - 數字級距: 25, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950
102
+ gray: (
103
+ 25: #FBFBFC,
104
+ 50: #F9FAFB,
105
+ 100: #F3F4F6,
106
+ 200: #E5E7EB,
107
+ 300: #C9CBD4,
108
+ 400: #9DA4AE,
109
+ 500: #6C737F,
110
+ 600: #525A61,
111
+ 700: #404750,
112
+ 800: #29313B,
113
+ 900: #191E26,
114
+ 950: #101319,
115
+ ),
116
+ // 白色系 - 特殊級距: white, white-alpha-10/20/.../90
117
+ white-base: (
118
+ white: #FFF,
119
+ white-alpha-5: rgb(255 255 255 / 5%),
120
+ white-alpha-10: rgb(255 255 255 / 10%),
121
+ white-alpha-20: rgb(255 255 255 / 20%),
122
+ white-alpha-30: rgb(255 255 255 / 30%),
123
+ white-alpha-40: rgb(255 255 255 / 40%),
124
+ white-alpha-50: rgb(255 255 255 / 50%),
125
+ white-alpha-60: rgb(255 255 255 / 60%),
126
+ white-alpha-70: rgb(255 255 255 / 70%),
127
+ white-alpha-80: rgb(255 255 255 / 80%),
128
+ white-alpha-90: rgb(255 255 255 / 90%),
129
+ ),
130
+ // 黑色系 - 特殊級距: black, black-alpha-3/5/8/10/20/.../90
131
+ black-base: (
132
+ black: #000,
133
+ black-alpha-3: rgb(0 0 0 / 3%),
134
+ black-alpha-5: rgb(0 0 0 / 5%),
135
+ black-alpha-8: rgb(0 0 0 / 8%),
136
+ black-alpha-10: rgb(0 0 0 / 10%),
137
+ black-alpha-20: rgb(0 0 0 / 20%),
138
+ black-alpha-30: rgb(0 0 0 / 30%),
139
+ black-alpha-40: rgb(0 0 0 / 40%),
140
+ black-alpha-50: rgb(0 0 0 / 50%),
141
+ black-alpha-60: rgb(0 0 0 / 60%),
142
+ black-alpha-70: rgb(0 0 0 / 70%),
143
+ black-alpha-80: rgb(0 0 0 / 80%),
144
+ black-alpha-90: rgb(0 0 0 / 90%),
145
+ ),
146
+ // 品牌色透明度 - 特殊級距: brand-alpha-10/20/.../90
147
+ brand-base: (
148
+ brand-alpha-10: rgb(93 116 233 / 10%),
149
+ brand-alpha-20: rgb(93 116 233 / 20%),
150
+ brand-alpha-30: rgb(93 116 233 / 30%),
151
+ brand-alpha-40: rgb(93 116 233 / 40%),
152
+ brand-alpha-50: rgb(93 116 233 / 50%),
153
+ brand-alpha-60: rgb(93 116 233 / 60%),
154
+ brand-alpha-70: rgb(93 116 233 / 70%),
155
+ brand-alpha-80: rgb(93 116 233 / 80%),
156
+ brand-alpha-90: rgb(93 116 233 / 90%),
157
+ ),
158
+ );
159
+
160
+ // 驗證分類名稱是否有效
161
+ @function is-valid-category($category) {
162
+ @return list.index($primitive-categories, $category) != null;
163
+ }
164
+
165
+ // 驗證顏色值是否已定義
166
+ @function is-color-defined($color) {
167
+ @return $color != null;
168
+ }
169
+
170
+ // 取得特定分類的所有有效 scale 名稱
171
+ @function get-valid-scales($category, $primitives: $default-primitives) {
172
+ $category-colors: map.get($primitives, $category);
173
+
174
+ @if not $category-colors {
175
+ @return ();
176
+ }
177
+
178
+ @return map.keys($category-colors);
179
+ }
180
+
181
+ // 驗證 scale 是否對該分類有效
182
+ @function is-valid-scale-for-category($category, $scale, $primitives: $default-primitives) {
183
+ $valid-scales: get-valid-scales($category, $primitives);
184
+
185
+ @return list.index($valid-scales, $scale) != null;
186
+ }
187
+
188
+ // 取得特定分類的所有顏色
189
+ @function get-category-colors($category, $primitives: $default-primitives) {
190
+ @if not is-valid-category($category) {
191
+ @error 'Invalid category "#{$category}". Valid categories are: #{$primitive-categories}';
192
+ }
193
+
194
+ @return map.get($primitives, $category);
195
+ }
196
+
197
+ // 取得特定分類和級距的顏色值
198
+ @function get-color($category, $scale, $primitives: $default-primitives) {
199
+ @if not is-valid-category($category) {
200
+ @error 'Invalid category "#{$category}". Valid categories are: #{$primitive-categories}';
201
+ }
202
+
203
+ @if not is-valid-scale-for-category($category, $scale, $primitives) {
204
+ $valid-scales: get-valid-scales($category, $primitives);
205
+
206
+ @error 'Invalid scale "#{$scale}" for category "#{$category}". Valid scales are: #{$valid-scales}';
207
+ }
208
+
209
+ $category-colors: map.get($primitives, $category);
210
+ $color: map.get($category-colors, $scale);
211
+
212
+ @if not is-color-defined($color) {
213
+ @error 'Color not defined for #{$category}-#{$scale}';
214
+ }
215
+
216
+ @return $color;
217
+ }
218
+
219
+ // 取得 CSS 變數名稱
220
+ // e.g: --mzn-color-primitive-brand-500
221
+ @function get-var-name($category, $scale) {
222
+ @if $primitive-prefix == '' {
223
+ @return --#{$category}-#{$scale};
224
+ }
225
+
226
+ @return --#{$primitive-prefix}-#{$category}-#{$scale};
227
+ }
228
+
229
+ // 使用 CSS 變數引用顏色
230
+ @function variable($category, $scale) {
231
+ @if not is-valid-category($category) {
232
+ @error 'Invalid category "#{$category}". Valid categories are: #{$primitive-categories}';
233
+ }
234
+
235
+ @if not is-valid-scale-for-category($category, $scale) {
236
+ $valid-scales: get-valid-scales($category);
237
+
238
+ @error 'Invalid scale "#{$scale}" for category "#{$category}". Valid scales are: #{$valid-scales}';
239
+ }
240
+
241
+ @return var(#{get-var-name($category, $scale)});
242
+ }
243
+
244
+ // 生成所有 CSS 變數
245
+ @mixin variables($primitives: (), $skip-undefined: true) {
246
+ $primitives: map.deep-merge($default-primitives, $primitives);
247
+
248
+ @each $category, $colors in $primitives {
249
+ @if not is-valid-category($category) {
250
+ @error 'Invalid category "#{$category}" in primitives map. Valid categories are: #{$primitive-categories}';
251
+ }
252
+
253
+ @each $scale, $color in $colors {
254
+ @if not is-valid-scale-for-category($category, $scale, $primitives) {
255
+ $valid-scales: get-valid-scales($category, $primitives);
256
+
257
+ @error 'Invalid scale "#{$scale}" for category "#{$category}". Valid scales are: #{$valid-scales}';
258
+ }
259
+
260
+ @if is-color-defined($color) {
261
+ #{get-var-name($category, $scale)}: #{$color};
262
+ } @else if not $skip-undefined {
263
+ @error 'Color not defined for #{$category}-#{$scale}';
264
+ }
265
+ }
266
+ }
267
+ }