summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVolpeon <git@volpeon.ink>2024-06-23 19:05:33 +0200
committerVolpeon <git@volpeon.ink>2024-06-23 19:05:33 +0200
commit64a841c69ead262666dd0754218585d0f05cf735 (patch)
tree2e6d3462d544c18d51e1e9930a2d95f98111e219
parentUpdate (diff)
downloadiro-design-64a841c69ead262666dd0754218585d0f05cf735.tar.gz
iro-design-64a841c69ead262666dd0754218585d0f05cf735.tar.bz2
iro-design-64a841c69ead262666dd0754218585d0f05cf735.zip
WIP: New color calculations
-rw-r--r--src/.old/objects/_checkbox.scss249
-rw-r--r--src/.old/objects/_radio.scss176
-rw-r--r--src/_config.scss62
-rw-r--r--src/_declare-vars.scss8
-rw-r--r--src/_functions.scss82
-rw-r--r--src/_objects.scss4
-rw-r--r--src/functions/_colors.scss159
-rw-r--r--src/objects/_badge.scss32
-rw-r--r--src/objects/_button.scss74
-rw-r--r--src/objects/_checkbox.scss271
-rw-r--r--src/objects/_radio.scss197
-rw-r--r--src/objects/_text-field.scss11
-rw-r--r--src/scopes/_links.scss9
-rw-r--r--tpl/objects/checkbox.pug1
-rw-r--r--tpl/objects/radio.pug1
-rw-r--r--tpl/views/button.pug2
-rw-r--r--tpl/views/links.pug6
17 files changed, 818 insertions, 526 deletions
diff --git a/src/.old/objects/_checkbox.scss b/src/.old/objects/_checkbox.scss
deleted file mode 100644
index b3bb34d..0000000
--- a/src/.old/objects/_checkbox.scss
+++ /dev/null
@@ -1,249 +0,0 @@
1@use 'iro-sass/src/index' as iro;
2@use '../functions' as fn;
3
4@include iro.props-namespace('checkbox') {
5 @include iro.props-store((
6 --dims: (
7 --size: fn.global-dim(--size --175),
8 --label-gap: fn.global-dim(--size --125),
9 --border: fn.global-dim(--border --medium),
10 --pad-x: fn.global-dim(--size --65),
11 --pad-y: fn.global-dim(--size --65),
12 ),
13 ), 'dims');
14
15 @include iro.props-store((
16 --colors: (
17 --box-border: fn.global-color(--fg-hi),
18 --box-bg: fn.global-color(--bg-hi),
19
20 --hover: (
21 --label: fn.global-color(--fg-lo),
22 --box-border: fn.global-color(--fg),
23 ),
24 --accent: (
25 --box-border: fn.global-color(--accent --primary --solid --bg),
26
27 --hover: (
28 --box-border: fn.global-color(--accent --primary --solid --obj),
29 ),
30 ),
31 --key-focus: (
32 --label: fn.global-color(--focus --text),
33 --box-border: fn.global-color(--focus --fill),
34 --shadow: fn.global-color(--focus --shadow),
35 ),
36 --disabled: (
37 --label: fn.global-color(--fg-hi3),
38 --box-border: fn.global-color(--obj-lo),
39 --box-bg: fn.global-color(--bg-hi),
40 )
41 ),
42 ), 'colors');
43
44 @include iro.bem-object(iro.props-namespace()) {
45 display: inline-flex;
46 position: relative;
47 align-items: flex-start;
48 margin-right: calc(-1 * fn.dim(--pad-x));
49 margin-left: calc(-1 * fn.dim(--pad-x));
50 padding: fn.dim(--pad-y) fn.dim(--pad-x);
51
52 @include iro.bem-elem('box') {
53 display: block;
54 position: relative;
55 flex: 0 0 auto;
56 width: fn.dim(--size);
57 height: fn.dim(--size);
58 margin-top: calc(.5 * (fn.global-dim(--font --standard --line-height) * 1em - fn.dim(--size)));
59 border-radius: fn.dim(--border);
60 background-color: fn.color(--box-border);
61
62 &::before,
63 &::after {
64 content: '';
65 display: block;
66 position: absolute;
67 }
68
69 &::before {
70 z-index: 2;
71 top: fn.dim(--border);
72 left: fn.dim(--border);
73 width: calc(fn.dim(--size) - 2 * fn.dim(--border));
74 height: calc(fn.dim(--size) - 2 * fn.dim(--border));
75 transition: transform .2s ease;
76 background-color: fn.color(--box-bg);
77 }
78
79 &::after {
80 z-index: 3;
81 top: calc(.5 * fn.dim(--size) - 1px);
82 left: calc(1.5 * fn.dim(--border));
83 box-sizing: border-box;
84 width: calc(fn.dim(--size) - 3 * fn.dim(--border));
85 height: 0;
86 transform: scale(0);
87 transition: transform .2s ease;
88 border-width: 0 2px 2px 0;
89 border-style: solid;
90 border-radius: 2px;
91 border-color: fn.color(--box-bg);
92 }
93 }
94
95 @include iro.bem-elem('check-icon') {
96 display: block;
97 position: absolute;
98 z-index: 2;
99 top: calc(1 * fn.dim(--border));
100 left: calc(1 * fn.dim(--border));
101 width: calc(100% - 2 * fn.dim(--border));
102 height: calc(100% - 2 * fn.dim(--border));
103 margin: 0;
104 transform: scale(0);
105 transform-origin: 40% 90%;
106 transition: transform .2s ease;
107 stroke-width: iro.fn-px-to-rem(3px);
108 color: fn.color(--box-bg);
109 }
110
111 @include iro.bem-elem('label') {
112 align-self: baseline;
113 margin-left: fn.dim(--label-gap);
114 }
115
116 @include iro.bem-elem('native') {
117 position: absolute;
118 top: 0;
119 left: 0;
120 width: 100%;
121 height: 100%;
122 margin: 0;
123 padding: 0;
124 overflow: hidden;
125 opacity: .0001;
126
127 &:hover {
128 @include iro.bem-sibling-elem('label') {
129 color: fn.color(--hover --label);
130 }
131
132 @include iro.bem-sibling-elem('box') {
133 background-color: fn.color(--hover --box-border);
134 }
135 }
136
137 &:checked {
138 @include iro.bem-sibling-elem('box') {
139 &::before {
140 transform: scale(0);
141 }
142
143 @include iro.bem-elem('check-icon') {
144 transform: scale(1);
145 }
146 }
147 }
148
149 &:indeterminate {
150 @include iro.bem-sibling-elem('box') {
151 &::before {
152 transform: scale(0);
153 }
154
155 &::after {
156 transform: scale(1);
157 }
158
159 @include iro.bem-elem('check-icon') {
160 transform: scale(0);
161 }
162 }
163 }
164
165 &:disabled {
166 @include iro.bem-sibling-elem('label') {
167 color: fn.color(--disabled --label);
168 }
169
170 @include iro.bem-sibling-elem('box') {
171 background-color: fn.color(--disabled --box-border);
172
173 &::before {
174 background-color: fn.color(--disabled --box-bg);
175 }
176 }
177 }
178
179 @include iro.bem-at-theme('keyboard') {
180 &:focus {
181 @include iro.bem-sibling-elem('label') {
182 color: fn.color(--key-focus --label);
183 }
184
185 @include iro.bem-sibling-elem('box') {
186 background-color: fn.color(--key-focus --box-border);
187 box-shadow: fn.color(--key-focus --shadow);
188 }
189 }
190 }
191 }
192
193 @include iro.bem-modifier('standalone') {
194 @include iro.bem-elem('box') {
195 margin-top: 0;
196 }
197 }
198
199 @include iro.bem-modifier('accent') {
200 @include iro.bem-elem('native') {
201 &:checked {
202 @include iro.bem-sibling-elem('box') {
203 background-color: fn.color(--accent --box-border);
204 }
205
206 &:hover {
207 @include iro.bem-sibling-elem('box') {
208 background-color: fn.color(--accent --hover --box-border);
209 }
210 }
211 }
212
213 &:indeterminate {
214 @include iro.bem-sibling-elem('box') {
215 background-color: fn.color(--accent --box-border);
216 }
217
218 &:hover {
219 @include iro.bem-sibling-elem('box') {
220 background-color: fn.color(--accent --hover --box-border);
221 }
222 }
223 }
224
225 &:disabled {
226 @include iro.bem-sibling-elem('box') {
227 background-color: fn.color(--disabled --box-border);
228
229 &::before {
230 background-color: fn.color(--disabled --box-bg);
231 }
232 }
233
234 &:checked {
235 @include iro.bem-sibling-elem('box') {
236 background-color: fn.color(--disabled --box-border);
237 }
238 }
239
240 &:indeterminate {
241 @include iro.bem-sibling-elem('box') {
242 background-color: fn.color(--disabled --box-border);
243 }
244 }
245 }
246 }
247 }
248 }
249}
diff --git a/src/.old/objects/_radio.scss b/src/.old/objects/_radio.scss
deleted file mode 100644
index 5af7a12..0000000
--- a/src/.old/objects/_radio.scss
+++ /dev/null
@@ -1,176 +0,0 @@
1@use 'iro-sass/src/index' as iro;
2@use '../functions' as fn;
3
4@include iro.props-namespace('radio') {
5 @include iro.props-store((
6 --dims: (
7 --diameter: calc(fn.global-dim(--size --175) + 1px),
8 --label-gap: fn.global-dim(--size --125),
9 --border: fn.global-dim(--border --medium),
10 --pad-x: fn.global-dim(--size --65),
11 --pad-y: fn.global-dim(--size --65),
12 ),
13 ), 'dims');
14
15 @include iro.props-store((
16 --colors: (
17 --circle-border: fn.global-color(--fg-hi),
18 --circle-bg: fn.global-color(--bg-hi),
19
20 --hover: (
21 --label: fn.global-color(--fg-lo),
22 --circle-border: fn.global-color(--fg),
23 ),
24 --accent: (
25 --circle-border: fn.global-color(--accent --primary --solid --bg),
26
27 --hover: (
28 --circle-border: fn.global-color(--accent --primary --solid --obj),
29 ),
30 ),
31 --key-focus: (
32 --label: fn.global-color(--focus --text),
33 --circle-border: fn.global-color(--focus --fill),
34 --shadow: fn.global-color(--focus --shadow),
35 ),
36 --disabled: (
37 --label: fn.global-color(--fg-hi3),
38 --circle-border: fn.global-color(--obj-lo),
39 --circle-bg: fn.global-color(--bg-hi),
40 )
41 ),
42 ), 'colors');
43
44 @include iro.bem-object(iro.props-namespace()) {
45 display: inline-flex;
46 position: relative;
47 align-items: flex-start;
48 margin-right: calc(-1 * fn.dim(--pad-x));
49 margin-left: calc(-1 * fn.dim(--pad-x));
50 padding: fn.dim(--pad-y) fn.dim(--pad-x);
51
52 @include iro.bem-elem('circle') {
53 display: block;
54 box-sizing: border-box;
55 flex: 0 0 auto;
56 width: fn.dim(--diameter);
57 height: fn.dim(--diameter);
58 margin-top: calc(.5 * (fn.global-dim(--font --standard --line-height) * 1em - fn.dim(--diameter)));
59 border-radius: 2em;
60 background-color: fn.color(--circle-border);
61
62 &::after {
63 content: '';
64 display: block;
65 position: relative;
66 top: fn.dim(--border);
67 left: fn.dim(--border);
68 width: calc(fn.dim(--diameter) - 2 * fn.dim(--border));
69 height: calc(fn.dim(--diameter) - 2 * fn.dim(--border));
70 transition: transform .2s ease;
71 border-radius: fn.dim(--diameter);
72 background-color: fn.color(--circle-bg);
73 }
74 }
75
76 @include iro.bem-elem('label') {
77 align-self: baseline;
78 margin-left: fn.dim(--label-gap);
79 }
80
81 @include iro.bem-elem('native') {
82 position: absolute;
83 top: 0;
84 left: 0;
85 width: 100%;
86 height: 100%;
87 margin: 0;
88 padding: 0;
89 overflow: hidden;
90 opacity: .0001;
91
92 &:hover {
93 @include iro.bem-sibling-elem('label') {
94 color: fn.color(--hover --label);
95 }
96
97 @include iro.bem-sibling-elem('circle') {
98 background-color: fn.color(--hover --circle-border);
99 }
100 }
101
102 &:checked {
103 @include iro.bem-sibling-elem('circle') {
104 &::after {
105 transform: scale(.44);
106 }
107 }
108 }
109
110 &:disabled {
111 @include iro.bem-sibling-elem('label') {
112 color: fn.color(--disabled --label);
113 }
114
115 @include iro.bem-sibling-elem('circle') {
116 background-color: fn.color(--disabled --circle-border);
117
118 &::after {
119 background-color: fn.color(--disabled --circle-bg);
120 }
121 }
122 }
123
124 @include iro.bem-at-theme('keyboard') {
125 &:focus {
126 @include iro.bem-sibling-elem('label') {
127 color: fn.color(--key-focus --label);
128 }
129
130 @include iro.bem-sibling-elem('circle') {
131 background-color: fn.color(--key-focus --circle-border);
132 box-shadow: fn.color(--key-focus --shadow);
133 }
134 }
135 }
136 }
137
138 @include iro.bem-modifier('standalone') {
139 @include iro.bem-elem('circle') {
140 margin-top: 0;
141 }
142 }
143
144 @include iro.bem-modifier('accent') {
145 @include iro.bem-elem('native') {
146 &:checked {
147 @include iro.bem-sibling-elem('circle') {
148 background-color: fn.color(--accent --circle-border);
149 }
150
151 &:hover {
152 @include iro.bem-sibling-elem('circle') {
153 background-color: fn.color(--accent --hover --circle-border);
154 }
155 }
156 }
157
158 &:disabled {
159 @include iro.bem-sibling-elem('circle') {
160 background-color: fn.color(--disabled --circle-border);
161
162 &::after {
163 background-color: fn.color(--disabled --circle-bg);
164 }
165 }
166
167 &:checked {
168 @include iro.bem-sibling-elem('circle') {
169 background-color: fn.color(--disabled --circle-border);
170 }
171 }
172 }
173 }
174 }
175 }
176}
diff --git a/src/_config.scss b/src/_config.scss
index 3cffbfd..950ff3b 100644
--- a/src/_config.scss
+++ b/src/_config.scss
@@ -23,7 +23,7 @@ media.$unit-intervals: (
23 23
24res.$named-viewports: media.$breakpoints; 24res.$named-viewports: media.$breakpoints;
25 25
26$palette-precision: 5 !default; 26$palette-precision: 0.01 !default;
27 27
28$static-colors: ( 28$static-colors: (
29 --base: hsl(0, 0%, 97%), 29 --base: hsl(0, 0%, 97%),
@@ -56,29 +56,29 @@ $static-colors: (
56$theme-light: ( 56$theme-light: (
57 --contrasts: ( 57 --contrasts: (
58 --grays: ( 58 --grays: (
59 --50: 1.1, 59 --50: -10,
60 --75: 1.04, 60 --75: -5,
61 --100: 1, 61 --100: 0,
62 62
63 --200: -1.15, 63 --200: 13.2375,
64 --300: -1.35, 64 --300: 26.475,
65 --400: -1.7, 65 --400: 39.7125,
66 66
67 --500: -2.4, 67 --500: 52.95,
68 --600: -3.3, 68 --600: 66.1875,
69 --700: -6, 69 --700: 79.425,
70 --800: -13, 70 --800: 92.6625,
71 --900: -20, 71 --900: 105.9,
72 ), 72 ),
73 73
74 --colors: ( 74 --colors: (
75 --100: -1.08, 75 --100: -1.18,
76 --200: -1.20, 76 --200: -1.30,
77 --300: -1.33, 77 --300: -1.43,
78 --400: -1.58, 78 --400: -1.68,
79 --500: -1.92, 79 --500: -2.00,
80 --600: -2.39, 80 --600: -2.45,
81 --700: -3.01, 81 --700: -3.04,
82 --800: -3.87, 82 --800: -3.87,
83 --900: -5.07, 83 --900: -5.07,
84 --1000: -6.72, 84 --1000: -6.72,
@@ -90,7 +90,7 @@ $theme-light: (
90 90
91 --ranges: ( 91 --ranges: (
92 --full: 1, 92 --full: 1,
93 --muted: 0.8, 93 --muted: .2,
94 ), 94 ),
95 95
96 --palettes: ( 96 --palettes: (
@@ -104,15 +104,17 @@ $theme-light: (
104 104
105 --semantic: ( 105 --semantic: (
106 --accent: --blue, 106 --accent: --blue,
107 --positive: --green,
108 --negative: --red,
109 --warning: --yellow,
110
111 --accent-static: --blue-static, 107 --accent-static: --blue-static,
108 --positive: --green,
112 --positive-static: --green-static, 109 --positive-static: --green-static,
110 --negative: --red,
113 --negative-static: --red-static, 111 --negative-static: --red-static,
112 --warning: --yellow,
114 --warning-static: --yellow-static, 113 --warning-static: --yellow-static,
115 114
115 --focus: --yellow,
116 --focus-static: --yellow-static,
117
116 --bg-l2: --base --50, 118 --bg-l2: --base --50,
117 --bg-l1: --base --100, 119 --bg-l1: --base --100,
118 --bg-base: --base --200, 120 --bg-base: --base --200,
@@ -166,7 +168,7 @@ $theme-dark: (
166 168
167 --ranges: ( 169 --ranges: (
168 --full: 1, 170 --full: 1,
169 --muted: 0.8, 171 --muted: .2,
170 ), 172 ),
171 173
172 --palettes: ( 174 --palettes: (
@@ -180,15 +182,17 @@ $theme-dark: (
180 182
181 --semantic: ( 183 --semantic: (
182 --accent: --blue, 184 --accent: --blue,
183 --positive: --green,
184 --negative: --red,
185 --warning: --yellow,
186
187 --accent-static: --blue-static, 185 --accent-static: --blue-static,
186 --positive: --green,
188 --positive-static: --green-static, 187 --positive-static: --green-static,
188 --negative: --red,
189 --negative-static: --red-static, 189 --negative-static: --red-static,
190 --warning: --yellow,
190 --warning-static: --yellow-static, 191 --warning-static: --yellow-static,
191 192
193 --focus: --yellow,
194 --focus-static: --yellow-static,
195
192 --bg-base: --base --50, 196 --bg-base: --base --50,
193 --bg-l1: --base --75, 197 --bg-l1: --base --75,
194 --bg-l2: --base --100, 198 --bg-l2: --base --100,
diff --git a/src/_declare-vars.scss b/src/_declare-vars.scss
index 4574333..2c845fa 100644
--- a/src/_declare-vars.scss
+++ b/src/_declare-vars.scss
@@ -78,7 +78,7 @@
78 ), 78 ),
79 79
80 --font-size: ( 80 --font-size: (
81 --50: iro.fn-px-to-rem(11px), 81 --50: iro.fn-px-to-rem(12px),
82 --75: iro.fn-px-to-rem(13px), 82 --75: iro.fn-px-to-rem(13px),
83 --100: iro.fn-px-to-rem(14px), 83 --100: iro.fn-px-to-rem(14px),
84 --150: iro.fn-px-to-rem(16px), 84 --150: iro.fn-px-to-rem(16px),
@@ -102,8 +102,8 @@
102 102
103 --rounding: 4px, 103 --rounding: 4px,
104 104
105 --focus: ( 105 --key-focus: (
106 --outline-width: 3px, 106 --border: 4px,
107 ), 107 ),
108 108
109 --paragraph: ( 109 --paragraph: (
@@ -183,7 +183,6 @@
183 $palette, 183 $palette,
184 map.get(config.$static-colors, --contrasts), 184 map.get(config.$static-colors, --contrasts),
185 1, 185 1,
186 false,
187 map.get(config.$static-colors, --base), 186 map.get(config.$static-colors, --base),
188 ), 187 ),
189 ), 188 ),
@@ -204,7 +203,6 @@
204 $base-color, 203 $base-color,
205 map.get($theme, --contrasts, $contrasts), 204 map.get($theme, --contrasts, $contrasts),
206 map.get($theme, --ranges, $ranges), 205 map.get($theme, --ranges, $ranges),
207 true,
208 list.nth(map.get($theme, --palettes, --base), 1), 206 list.nth(map.get($theme, --palettes, --base), 1),
209 ), 207 ),
210 ), 208 ),
diff --git a/src/_functions.scss b/src/_functions.scss
index 8553833..2d4aeef 100644
--- a/src/_functions.scss
+++ b/src/_functions.scss
@@ -2,11 +2,15 @@
2@use 'sass:math'; 2@use 'sass:math';
3@use 'sass:map'; 3@use 'sass:map';
4@use 'sass:list'; 4@use 'sass:list';
5@use 'iro-sass/src/index' as iro; 5@use 'sass:meta';
6@use 'config'; 6
7@use '@oddbird/blend'; 7@use '@oddbird/blend';
8@use '@oddbird/blend/sass/convert' as blend-convert; 8@use '@oddbird/blend/sass/convert' as blend-convert;
9 9
10@use 'functions/colors' as iro-colors;
11@use 'iro-sass/src/index' as iro;
12@use 'config';
13
10@function color($key, $tree: iro.$props-default-tree, $default: null, $global: false) { 14@function color($key, $tree: iro.$props-default-tree, $default: null, $global: false) {
11 @return iro.props-get(list.join(--colors, $key), $tree, $default, $global); 15 @return iro.props-get(list.join(--colors, $key), $tree, $default, $global);
12} 16}
@@ -107,7 +111,9 @@
107 @return $result; 111 @return $result;
108} 112}
109 113
110@function palette($base-color, $contrasts, $range: 1, $desaturate: true, $reference-color: $base-color) { 114@function palette-old($base-color, $contrasts, $chroma-range: 1, $reference-color: $base-color) {
115 $chroma-range: 1 - $chroma-range;
116
111 $palette: (); 117 $palette: ();
112 118
113 @if list.nth(list.nth($contrasts, 1), 2) > list.nth(list.nth($contrasts, list.length($contrasts)), 2) { 119 @if list.nth(list.nth($contrasts, 1), 2) > list.nth(list.nth($contrasts, list.length($contrasts)), 2) {
@@ -115,12 +121,25 @@
115 } 121 }
116 122
117 $reference-lightness: blend.lightness($reference-color); 123 $reference-lightness: blend.lightness($reference-color);
118 $i: -100%; 124 $i: 0;
125
126 @while $i <= 1 {
127 $l: $i * 200% - 100%;
128 $c: 0%;
129
130 @if $chroma-range != 0 {
131 $c: $i * 2 - 1;
132 $c: if(
133 $reference-lightness >= 50%,
134 math.clamp(0, $c, 1),
135 math.clamp(0, -1 * $c, 1)
136 );
137 $c: $chroma-range * $c * -100%;
138 }
139
140 $palette: list.append($palette, blend.scale($base-color, $l: $l, $c: $c));
119 141
120 @while $i <= 100% { 142 $i: $i + config.$palette-precision;
121 $c: if($desaturate, .8 * if($reference-lightness >= 50%, -1 * math.clamp(0%, $i, 100%), math.clamp(-100%, $i, 0%)), 0%);
122 $palette: list.append($palette, blend.scale($base-color, $l: $range * $i, $c: $c));
123 $i: $i + config.$palette-precision;
124 } 143 }
125 144
126 $palette: multi-contrast($base-color, $palette, $contrasts, $reference-color); 145 $palette: multi-contrast($base-color, $palette, $contrasts, $reference-color);
@@ -132,6 +151,53 @@
132 @return $palette; 151 @return $palette;
133} 152}
134 153
154@function palette($base-color, $contrasts, $chroma-range: 1, $reference-color: $base-color) {
155 $base-lch: blend-convert.Lab_to_LCH(
156 iro-colors.lin_sRGB_to_Oklab(
157 blend-convert.lin_sRGB(
158 blend-convert.sassToRgb($base-color)
159 )
160 )
161 );
162 $ref-y: iro-colors.apca_sRGB_to_Y($reference-color);
163
164 $palette: ();
165
166 @each $key, $contrast in $contrasts {
167 $y: iro-colors.apcaReverse($contrast, $ref-y);
168 $l: list.nth($base-lch, 1);
169
170 @if $y != false {
171 @debug $contrast, $ref-y, $y, iro-colors.apca_Y_to_sRGB($y);
172
173 $y-lch: blend-convert.Lab_to_LCH(
174 iro-colors.lin_sRGB_to_Oklab(
175 blend-convert.lin_sRGB(
176 blend-convert.sassToRgb(
177 iro-colors.apca_Y_to_sRGB($y)
178 )
179 )
180 )
181 );
182 $l: list.nth($y-lch, 1);
183 } @else {
184 @debug $contrast, $ref-y, $y;
185 }
186
187 $color: oklch($l * 100% list.nth($base-lch, 2) list.nth($base-lch, 3));
188 //$color: blend-convert.LCH_to_Lab($color);
189 //$color: iro-colors.Oklab_to_lin_sRGB($color);
190 //$color: blend-convert();
191
192 $palette: map.set($palette, $key, $color);
193 $palette: map.set($palette, #{$key}-text, if($l > 50%, #000, #fff));
194 }
195
196 @debug '-------------------------------------------';
197
198 @return $palette;
199}
200
135@function px-to-em($size, $base: iro.$vars-root-size) { 201@function px-to-em($size, $base: iro.$vars-root-size) {
136 @return math.div($size, $base) * 1em; 202 @return math.div($size, $base) * 1em;
137} 203}
diff --git a/src/_objects.scss b/src/_objects.scss
index 3dd5176..0f510ff 100644
--- a/src/_objects.scss
+++ b/src/_objects.scss
@@ -6,8 +6,8 @@
6@use 'objects/button'; 6@use 'objects/button';
7@use 'objects/text-field'; 7@use 'objects/text-field';
8@use 'objects/field-label'; 8@use 'objects/field-label';
9// @use 'objects/radio'; 9@use 'objects/radio';
10// @use 'objects/checkbox'; 10@use 'objects/checkbox';
11// @use 'objects/switch'; 11// @use 'objects/switch';
12// @use 'objects/action-button'; 12// @use 'objects/action-button';
13// @use 'objects/overflow-button'; 13// @use 'objects/overflow-button';
diff --git a/src/functions/_colors.scss b/src/functions/_colors.scss
new file mode 100644
index 0000000..265c09a
--- /dev/null
+++ b/src/functions/_colors.scss
@@ -0,0 +1,159 @@
1/* stylelint-disable scss/dollar-variable-pattern */
2/* stylelint-disable scss/at-function-pattern */
3
4@use 'sass:color';
5@use 'sass:list';
6@use 'sass:map';
7@use 'sass:math';
8@use 'sass:meta';
9@use '@oddbird/blend/sass/utils/pow';
10
11$SA98G: (
12 mainTRC: 2.4,
13
14 sRco: 0.2126729,
15 sGco: 0.7151522,
16 sBco: 0.0721750,
17
18 normBG: 0.56,
19 normTXT: 0.57,
20 revTXT: 0.62,
21 revBG: 0.65,
22
23 blkThrs: 0.022,
24 blkClmp: 1.414,
25 scaleBoW: 1.14,
26 scaleWoB: 1.14,
27 loBoWoffset: 0.027,
28 loWoBoffset: 0.027,
29 deltaYmin: 0.0005,
30 loClip: 0.0001,
31
32 mFactor: 1.94685544331710,
33 mOffsetIn: 0.03873938165714010,
34 mExpAdj: 0.2833433964208690,
35 mOffsetOut: 0.3128657958707580,
36);
37
38@function lin_sRGB_to_Oklab($color) {
39 $r_: list.nth($color, 1);
40 $g_: list.nth($color, 2);
41 $b_: list.nth($color, 3);
42
43 $l: pow.cbrt(0.4122214708 * $r_ + 0.5363325363 * $g_ + 0.0514459929 * $b_);
44 $m: pow.cbrt(0.2119034982 * $r_ + 0.6806995451 * $g_ + 0.1073969566 * $b_);
45 $s: pow.cbrt(0.0883024619 * $r_ + 0.2817188376 * $g_ + 0.6299787005 * $b_);
46
47 @return (
48 0.2104542553 * $l + 0.7936177850 * $m - 0.0040720468 * $s,
49 1.9779984951 * $l - 2.4285922050 * $m + 0.4505937099 * $s,
50 0.0259040371 * $l + 0.7827717662 * $m - 0.8086757660 * $s,
51 );
52}
53
54@function Oklab_to_lin_sRGB($color) {
55 $l_: list.nth($color, 1);
56 $a_: list.nth($color, 2);
57 $b_: list.nth($color, 3);
58
59 $l: $l_ + 0.3963377774 * $a_ + 0.2158037573 * $b-;
60 $m: $l_ - 0.1055613458 * $a_ - 0.0638541728 * $b-;
61 $s: $l_ - 0.0894841775 * $a_ - 1.2914855480 * $b-;
62
63 $l: $l * $l * $l;
64 $m: $m * $m * $m;
65 $s: $s * $s * $s;
66
67 @return (
68 4.0767416621 * $l - 3.3077115913 * $m + 0.2309699292 * 4s,
69 -1.2684380046 * $l + 2.6097574011 * $m - 0.3413193965 * 4s,
70 -0.0041960863 * $l - 0.7034186147 * $m + 1.7076147010 * 4s,
71 );
72}
73
74@function apca_sRGB_to_Y($color) {
75 @return map.get($SA98G, sRco) * math.pow(math.div(color.red($color), 255), map.get($SA98G, mainTRC)) +
76 map.get($SA98G, sGco) * math.pow(math.div(color.green($color), 255), map.get($SA98G, mainTRC)) +
77 map.get($SA98G, sBco) * math.pow(math.div(color.blue($color), 255), map.get($SA98G, mainTRC));
78}
79
80@function apca_Y_to_sRGB($y) {
81 $c: math.round(math.pow($y, math.div(1, map.get($SA98G, mainTRC))) * 255);
82 @return rgb($c, $c, $c);
83}
84
85@function apcaContrast($txtY, $bgY) {
86 @if $txtY <= map.get($SA98G, blkThrs) {
87 $txtY: $txtY + math.pow(map.get($SA98G, blkThrs) - $txtY, map.get($SA98G, blkClmp));
88 }
89 @if $bgY <= map.get($SA98G, blkThrs) {
90 $bgY: $bgY + math.pow(map.get($SA98G, blkThrs) - $bgY, map.get($SA98G, blkClmp));
91 }
92
93 @if math.abs($bgY - $txtY) < map.get($SA98G, deltaYmin) {
94 @return 0;
95 }
96
97 $outputContrast: 0;
98
99 @if $bgY > $txtY {
100 $SAPC: map.get($SA98G, scaleBoW) * (math.pow($bgY, map.get($SA98G, normBG)) - math.pow($txtY, map.get($SA98G, normTXT)));
101
102 @if $SAPC >= map.get($SA98G, loClip) {
103 $outputContrast: $SAPC - map.get($SA98G, loBoWoffset);
104 }
105 } @else {
106 $SAPC: map.get($SA98G, scaleWoB) * (math.pow($bgY, map.get($SA98G, revBG)) - math.pow($txtY, map.get($SA98G, revTXT)));
107
108 @if $SAPC <= -1 * map.get($SA98G, loClip) {
109 $outputContrast: $SAPC + map.get($SA98G, loWoBoffset);
110 }
111 }
112
113 @return outputContrast * 100.0;
114}
115
116@function apcaReverse($contrast, $knownY, $knownType: 'bg') {
117 $unknownY: $knownY;
118
119 $knownExp: 0;
120 $unknownExp: 0;
121
122 $scale: map.get($SA98G, if($contrast > 0, scaleBoW, scaleWoB));
123 $offset: map.get($SA98G, if($contrast > 0, loBoWoffset, loWoBoffset));
124
125 $contrast: math.div($contrast * 0.01 + $offset, $scale);
126
127 @if $knownY <= map.get($SA98G, blkThrs) {
128 $knownY: $knownY + math.pow(map.get($SA98G, blkThrs) - $knownY, map.get($SA98G, blkClmp));
129 }
130
131 @if $knownType == 'bg' {
132 $knownExp: map.get($SA98G, if($contrast > 0, normBG, revBG));
133 $unknownExp: map.get($SA98G, if($contrast > 0, normTXT, revTXT));
134 $unknownY: math.pow(math.pow($knownY, $knownExp) - $contrast, math.div(1, $unknownExp));
135 } @else {
136 $knownExp: map.get($SA98G, if($contrast > 0, normTXT, revTXT));
137 $unknownExp: map.get($SA98G, if($contrast > 0, normBG, revBG));
138 $unknownY: math.pow($contrast + math.pow($knownY, $knownExp), math.div(1, $unknownExp));
139 }
140
141 @if '#{$unknownY}' == '#{math.sqrt(-1)}' {
142 @return false;
143 }
144
145 @if $unknownY > 1.06 or $unknownY < 0 {
146 @return false;
147 }
148
149 @if $unknownY <= map.get($SA98G, blkThrs) {
150 $unknownY: math.pow(
151 ($unknownY + map.get($SA98G, mOffsetIn)) * map.get($SA98G, mFactor),
152 math.div(map.get($SA98G, mExpAdj), map.get($SA98G, blkClmp))
153 ) * math.div(1, map.get($SA98G, mFactor)) - map.get($SA98G, mOffsetOut);
154 }
155
156 $unknownY: math.max(math.min($unknownY, 1), 0);
157
158 @return $unknownY;
159}
diff --git a/src/objects/_badge.scss b/src/objects/_badge.scss
index 23ae305..6001564 100644
--- a/src/objects/_badge.scss
+++ b/src/objects/_badge.scss
@@ -6,29 +6,33 @@ $themes: accent positive negative warning;
6@include iro.props-namespace('badge') { 6@include iro.props-namespace('badge') {
7 @include iro.props-store(( 7 @include iro.props-store((
8 --dims: ( 8 --dims: (
9 --pad-b: fn.global-dim(--size --100), 9 --pad-b: fn.global-dim(--size --50),
10 --pad-i: fn.global-dim(--size --150), 10 --pad-i: fn.global-dim(--size --150),
11 --pad-i-pill: fn.global-dim(--size --200), 11 --pad-i-pill: fn.global-dim(--size --200),
12 --rounding: fn.global-dim(--rounding), 12 --rounding: fn.global-dim(--rounding),
13 --font-size: fn.global-dim(--font-size --100), 13 --font-size: fn.global-dim(--font-size --75),
14 14
15 --sm: ( 15 --sm: (
16 --pad-b: fn.global-dim(--size --50), 16 --pad-b: fn.global-dim(--size --25),
17 --pad-i: fn.global-dim(--size --115), 17 --pad-i: fn.global-dim(--size --115),
18 --pad-i-pill: fn.global-dim(--size --150), 18 --pad-i-pill: fn.global-dim(--size --150),
19 --font-size: fn.global-dim(--font-size --75), 19 --font-size: fn.global-dim(--font-size --50),
20 ), 20 ),
21 --lg: ( 21 --lg: (
22 --pad-b: fn.global-dim(--size --125), 22 --pad-b: fn.global-dim(--size --75),
23 --pad-i: fn.global-dim(--size --175), 23 --pad-i: fn.global-dim(--size --175),
24 --pad-i-pill: fn.global-dim(--size --225), 24 --pad-i-pill: fn.global-dim(--size --225),
25 --font-size: fn.global-dim(--font-size --150), 25 --font-size: fn.global-dim(--font-size --100),
26 ), 26 ),
27 --xl: ( 27 --xl: (
28 --pad-b: fn.global-dim(--size --160), 28 --pad-b: fn.global-dim(--size --100),
29 --pad-i: fn.global-dim(--size --225), 29 --pad-i: fn.global-dim(--size --225),
30 --pad-i-pill: fn.global-dim(--size --300), 30 --pad-i-pill: fn.global-dim(--size --300),
31 --font-size: fn.global-dim(--font-size --200), 31 --font-size: fn.global-dim(--font-size --150),
32 ),
33
34 --key-focus: (
35 --border: fn.global-dim(--key-focus --border),
32 ), 36 ),
33 ), 37 ),
34 --colors: ( 38 --colors: (
@@ -43,7 +47,7 @@ $themes: accent positive negative warning;
43 --key-focus: ( 47 --key-focus: (
44 --bg: fn.global-color(--base --50), 48 --bg: fn.global-color(--base --50),
45 --label: fn.global-color(--heading), 49 --label: fn.global-color(--heading),
46 --border: fn.global-color(--yellow-static --400), 50 --border: fn.global-color(--focus-static --400),
47 ), 51 ),
48 52
49 --quiet: ( 53 --quiet: (
@@ -63,13 +67,13 @@ $themes: accent positive negative warning;
63 @include iro.props-store(( 67 @include iro.props-store((
64 --colors: ( 68 --colors: (
65 --#{$theme}: ( 69 --#{$theme}: (
66 --bg: fn.global-color(--#{$theme} --800), 70 --bg: fn.global-color(--#{$theme}-static --800),
67 --label: fn.global-color(--#{$theme} --800-text), 71 --label: fn.global-color(--#{$theme}-static --800-text),
68 --hover: ( 72 --hover: (
69 --bg: fn.global-color(--#{$theme} --900), 73 --bg: fn.global-color(--#{$theme}-static --900),
70 ), 74 ),
71 --active: ( 75 --active: (
72 --bg: fn.global-color(--#{$theme} --900), 76 --bg: fn.global-color(--#{$theme}-static --900),
73 ), 77 ),
74 ), 78 ),
75 79
@@ -169,7 +173,7 @@ $themes: accent positive negative warning;
169 &:enabled { 173 &:enabled {
170 &:focus-visible { 174 &:focus-visible {
171 background-color: fn.color(--key-focus --bg); 175 background-color: fn.color(--key-focus --bg);
172 box-shadow: 0 0 0 fn.global-dim(--border --thick) fn.color(--key-focus --border); 176 box-shadow: 0 0 0 fn.dim(--key-focus --border) fn.color(--key-focus --border);
173 color: fn.color(--key-focus --label); 177 color: fn.color(--key-focus --label);
174 } 178 }
175 } 179 }
diff --git a/src/objects/_button.scss b/src/objects/_button.scss
index a823f17..1a0e52c 100644
--- a/src/objects/_button.scss
+++ b/src/objects/_button.scss
@@ -2,7 +2,7 @@
2@use 'iro-sass/src/index' as iro; 2@use 'iro-sass/src/index' as iro;
3@use '../functions' as fn; 3@use '../functions' as fn;
4 4
5$themes: primary accent positive negative warning; 5$themes: accent negative;
6 6
7@mixin button-variant($theme: null) { 7@mixin button-variant($theme: null) {
8 $key: if($theme == null, (), --#{$theme}); 8 $key: if($theme == null, (), --#{$theme});
@@ -19,6 +19,7 @@ $themes: primary accent positive negative warning;
19 &:link, 19 &:link,
20 &:visited, 20 &:visited,
21 &:enabled { 21 &:enabled {
22 border-color: fn.color(list.join($key, --outline-border));
22 background-color: transparent; 23 background-color: transparent;
23 color: fn.color(list.join($key, --outline-label)); 24 color: fn.color(list.join($key, --outline-label));
24 } 25 }
@@ -66,11 +67,16 @@ $themes: primary accent positive negative warning;
66 --pad-b: fn.global-dim(--size --150), 67 --pad-b: fn.global-dim(--size --150),
67 --font-size: fn.global-dim(--font-size --200), 68 --font-size: fn.global-dim(--font-size --200),
68 ), 69 ),
70
71 --key-focus: (
72 --border: fn.global-dim(--key-focus --border),
73 ),
69 ), 74 ),
70 --colors: ( 75 --colors: (
71 --bg: fn.global-color(--border-mute), 76 --bg: fn.global-color(--border-mute),
72 --label: fn.global-color(--text), 77 --label: fn.global-color(--text),
73 --outline-label: fn.global-color(--text), 78 --outline-border: fn.global-color(--border),
79 --outline-label: fn.global-color(--text),
74 80
75 --hover: ( 81 --hover: (
76 --bg: fn.global-color(--border), 82 --bg: fn.global-color(--border),
@@ -81,19 +87,21 @@ $themes: primary accent positive negative warning;
81 --label: fn.global-color(--heading), 87 --label: fn.global-color(--heading),
82 ), 88 ),
83 --disabled: ( 89 --disabled: (
84 --bg: fn.global-color(--border-mute), 90 --bg: fn.global-color(--border-mute),
85 --label: fn.global-color(--text-disabled), 91 --outline-border: fn.global-color(--border),
92 --label: fn.global-color(--text-disabled),
86 ), 93 ),
87 --key-focus: ( 94 --key-focus: (
88 --bg: fn.global-color(--base --50), 95 --bg: fn.global-color(--base --50),
89 --label: fn.global-color(--heading), 96 --label: fn.global-color(--heading),
90 --border: fn.global-color(--yellow-static --400), 97 --border: fn.global-color(--focus-static --400),
91 ), 98 ),
92 99
93 --primary: ( 100 --primary: (
94 --bg: fn.global-color(--base --800), 101 --bg: fn.global-color(--base --800),
95 --label: fn.global-color(--base --800-text), 102 --label: fn.global-color(--base --800-text),
96 --outline-label: fn.global-color(--text), 103 --outline-border: fn.global-color(--base --800),
104 --outline-label: fn.global-color(--text),
97 105
98 --hover: ( 106 --hover: (
99 --bg: fn.global-color(--base --900), 107 --bg: fn.global-color(--base --900),
@@ -108,26 +116,25 @@ $themes: primary accent positive negative warning;
108 )); 116 ));
109 117
110 @each $theme in $themes { 118 @each $theme in $themes {
111 @if $theme != primary { 119 @include iro.props-store((
112 @include iro.props-store(( 120 --colors: (
113 --colors: ( 121 --#{$theme}: (
114 --#{$theme}: ( 122 --bg: fn.global-color(--#{$theme}-static --900),
115 --bg: fn.global-color(--#{$theme} --900), 123 --label: fn.global-color(--#{$theme}-static --900-text),
116 --label: fn.global-color(--#{$theme} --900-text), 124 --outline-border: fn.global-color(--#{$theme} --900),
117 --outline-label: fn.global-color(--#{$theme} --1000), 125 --outline-label: fn.global-color(--#{$theme} --1000),
118 126
119 --hover: ( 127 --hover: (
120 --bg: fn.global-color(--#{$theme} --1000), 128 --bg: fn.global-color(--#{$theme}-static --1000),
121 --label: fn.global-color(--#{$theme} --1000-text), 129 --label: fn.global-color(--#{$theme}-static --1000-text),
122 ), 130 ),
123 --active: ( 131 --active: (
124 --bg: fn.global-color(--#{$theme} --1100), 132 --bg: fn.global-color(--#{$theme}-static --1100),
125 --label: fn.global-color(--#{$theme} --1100-text), 133 --label: fn.global-color(--#{$theme}-static --1100-text),
126 ),
127 ), 134 ),
128 ), 135 ),
129 )); 136 ),
130 } 137 ));
131 } 138 }
132 139
133 @include iro.bem-object(iro.props-namespace()) { 140 @include iro.bem-object(iro.props-namespace()) {
@@ -151,6 +158,7 @@ $themes: primary accent positive negative warning;
151 } 158 }
152 159
153 @include iro.bem-modifier('outline') { 160 @include iro.bem-modifier('outline') {
161 border-color: fn.color(--disabled --outline-border);
154 background-color: transparent; 162 background-color: transparent;
155 box-shadow: none; 163 box-shadow: none;
156 } 164 }
@@ -164,6 +172,10 @@ $themes: primary accent positive negative warning;
164 } 172 }
165 173
166 @include button-variant(); 174 @include button-variant();
175
176 @include iro.bem-modifier('primary') {
177 @include button-variant('primary');
178 }
167 179
168 @each $theme in $themes { 180 @each $theme in $themes {
169 @include iro.bem-modifier($theme) { 181 @include iro.bem-modifier($theme) {
@@ -177,7 +189,7 @@ $themes: primary accent positive negative warning;
177 &:focus-visible { 189 &:focus-visible {
178 border-color: fn.color(--key-focus --border); 190 border-color: fn.color(--key-focus --border);
179 background-color: fn.color(--key-focus --bg); 191 background-color: fn.color(--key-focus --bg);
180 box-shadow: 0 0 0 calc(fn.global-dim(--border --thick) - fn.dim(--border)) fn.color(--key-focus --border); 192 box-shadow: 0 0 0 calc(fn.dim(--key-focus --border) - fn.dim(--border)) fn.color(--key-focus --border);
181 color: fn.color(--key-focus --label); 193 color: fn.color(--key-focus --label);
182 } 194 }
183 } 195 }
diff --git a/src/objects/_checkbox.scss b/src/objects/_checkbox.scss
new file mode 100644
index 0000000..8e9e64c
--- /dev/null
+++ b/src/objects/_checkbox.scss
@@ -0,0 +1,271 @@
1@use 'iro-sass/src/index' as iro;
2@use '../functions' as fn;
3
4@include iro.props-namespace('checkbox') {
5 @include iro.props-store((
6 --dims: (
7 --size: fn.global-dim(--size --175),
8 --label-gap: fn.global-dim(--size --125),
9 --border: fn.global-dim(--border --medium),
10 --pad-i: fn.global-dim(--size --65),
11 --pad-b: fn.global-dim(--size --65),
12 --rounding: fn.global-dim(--rounding),
13 --spacing-sibling: fn.global-dim(--size --300),
14 ),
15 --colors: (
16 --box-border: fn.global-color(--text-mute),
17 --box-bg: fn.global-color(--base --75),
18
19 --hover: (
20 --label: fn.global-color(--heading),
21 --box-border: fn.global-color(--text),
22 ),
23 --accent: (
24 --box-border: fn.global-color(--accent --900),
25
26 --hover: (
27 --box-border: fn.global-color(--accent --1000),
28 ),
29 ),
30 --key-focus: (
31 --bg: fn.global-color(--focus-static --400),
32 --label: fn.global-color(--focus-static --400-text),
33 --box-border: fn.global-color(--focus-static --1000),
34 --box-bg: fn.global-color(--focus-static --1000-text),
35 ),
36 --disabled: (
37 --label: fn.global-color(--text-disabled),
38 --box-border: fn.global-color(--border-strong),
39 --box-bg: fn.global-color(--base --75),
40 )
41 ),
42 ));
43
44 @include iro.bem-object(iro.props-namespace()) {
45 display: inline-flex;
46 position: relative;
47 align-items: flex-start;
48 margin-inline: calc(-1 * fn.dim(--pad-i)) calc(fn.dim(--spacing-sibling) - fn.dim(--pad-b));
49 padding-block: fn.dim(--pad-b);
50 padding-inline: fn.dim(--pad-i);
51
52 @include iro.bem-elem('bg') {
53 display: block;
54 position: absolute;
55 z-index: -1;
56 inset-block: 0;
57 inset-inline: 0;
58 border-radius: fn.dim(--rounding);
59 pointer-events: none;
60 }
61
62 @include iro.bem-elem('box') {
63 display: block;
64 position: relative;
65 flex: 0 0 auto;
66 inline-size: fn.dim(--size);
67 block-size: fn.dim(--size);
68 margin-block-start: calc(.5 * (fn.global-dim(--font --standard --line-height) * 1em - fn.dim(--size)));
69 border-radius: fn.dim(--border);
70 background-color: fn.color(--box-border);
71
72 &::before,
73 &::after {
74 content: '';
75 display: block;
76 position: absolute;
77 }
78
79 &::before {
80 z-index: 2;
81 inset-block-start: fn.dim(--border);
82 inset-inline-start: fn.dim(--border);
83 inline-size: calc(fn.dim(--size) - 2 * fn.dim(--border));
84 block-size: calc(fn.dim(--size) - 2 * fn.dim(--border));
85 transition: transform .2s ease;
86 background-color: fn.color(--box-bg);
87 }
88
89 &::after {
90 z-index: 3;
91 inset-block-start: calc(.5 * fn.dim(--size) - 1px);
92 inset-inline-start: calc(1.5 * fn.dim(--border));
93 box-sizing: border-box;
94 inline-size: calc(fn.dim(--size) - 3 * fn.dim(--border));
95 block-size: 0;
96 transform: scale(0);
97 transition: transform .2s ease;
98 border-block-width: 0 2px;
99 border-inline-width: 0 2px;
100 border-style: solid;
101 border-radius: 2px;
102 border-color: fn.color(--box-bg);
103 }
104 }
105
106 @include iro.bem-elem('check-icon') {
107 display: block;
108 position: absolute;
109 z-index: 2;
110 inset-block-start: calc(1 * fn.dim(--border));
111 inset-inline-start: calc(1 * fn.dim(--border));
112 inline-size: calc(100% - 2 * fn.dim(--border));
113 block-size: calc(100% - 2 * fn.dim(--border));
114 margin: 0;
115 transform: scale(0);
116 transform-origin: 40% 90%;
117 transition: transform .2s ease;
118 stroke-width: iro.fn-px-to-rem(3px);
119 color: fn.color(--box-bg);
120 }
121
122 @include iro.bem-elem('label') {
123 align-self: baseline;
124 margin-left: fn.dim(--label-gap);
125 }
126
127 @include iro.bem-elem('native') {
128 position: absolute;
129 inset-block-start: 0;
130 inset-inline-start: 0;
131 inline-size: 100%;
132 block-size: 100%;
133 margin: 0;
134 padding: 0;
135 overflow: hidden;
136 opacity: .0001;
137
138 &:hover {
139 @include iro.bem-sibling-elem('label') {
140 color: fn.color(--hover --label);
141 }
142
143 @include iro.bem-sibling-elem('box') {
144 background-color: fn.color(--hover --box-border);
145 }
146 }
147
148 &:checked {
149 @include iro.bem-sibling-elem('box') {
150 &::before {
151 transform: scale(0);
152 }
153
154 @include iro.bem-elem('check-icon') {
155 transform: scale(1);
156 }
157 }
158 }
159
160 &:indeterminate {
161 @include iro.bem-sibling-elem('box') {
162 &::before {
163 transform: scale(0);
164 }
165
166 &::after {
167 transform: scale(1);
168 }
169
170 @include iro.bem-elem('check-icon') {
171 transform: scale(0);
172 }
173 }
174 }
175
176 &:disabled {
177 @include iro.bem-sibling-elem('label') {
178 color: fn.color(--disabled --label);
179 }
180
181 @include iro.bem-sibling-elem('box') {
182 background-color: fn.color(--disabled --box-border);
183
184 &::before {
185 background-color: fn.color(--disabled --box-bg);
186 }
187 }
188 }
189
190 &:focus-visible {
191 @include iro.bem-sibling-elem('bg') {
192 background-color: fn.color(--key-focus --bg);
193 }
194
195 @include iro.bem-sibling-elem('label') {
196 color: fn.color(--key-focus --label);
197 }
198
199 @include iro.bem-sibling-elem('box') {
200 background-color: fn.color(--key-focus --box-border);
201
202 &::before {
203 background-color: fn.color(--key-focus --box-bg);
204 }
205 }
206 }
207 }
208
209 @include iro.bem-modifier('standalone') {
210 @include iro.bem-elem('box') {
211 margin-block-start: 0;
212 }
213 }
214
215 @include iro.bem-modifier('accent') {
216 @include iro.bem-elem('native') {
217 &:checked {
218 @include iro.bem-sibling-elem('box') {
219 background-color: fn.color(--accent --box-border);
220 }
221
222 &:hover {
223 @include iro.bem-sibling-elem('box') {
224 background-color: fn.color(--accent --hover --box-border);
225 }
226 }
227 }
228
229 &:indeterminate {
230 @include iro.bem-sibling-elem('box') {
231 background-color: fn.color(--accent --box-border);
232 }
233
234 &:hover {
235 @include iro.bem-sibling-elem('box') {
236 background-color: fn.color(--accent --hover --box-border);
237 }
238 }
239 }
240
241 &:disabled {
242 @include iro.bem-sibling-elem('box') {
243 background-color: fn.color(--disabled --box-border);
244
245 &::before {
246 background-color: fn.color(--disabled --box-bg);
247 }
248 }
249
250 &:checked {
251 @include iro.bem-sibling-elem('box') {
252 background-color: fn.color(--disabled --box-border);
253 }
254 }
255
256 &:indeterminate {
257 @include iro.bem-sibling-elem('box') {
258 background-color: fn.color(--disabled --box-border);
259 }
260 }
261 }
262
263 &:focus-visible {
264 @include iro.bem-sibling-elem('box') {
265 background-color: fn.color(--key-focus --box-border);
266 }
267 }
268 }
269 }
270 }
271}
diff --git a/src/objects/_radio.scss b/src/objects/_radio.scss
new file mode 100644
index 0000000..5f461ce
--- /dev/null
+++ b/src/objects/_radio.scss
@@ -0,0 +1,197 @@
1@use 'iro-sass/src/index' as iro;
2@use '../functions' as fn;
3
4@include iro.props-namespace('radio') {
5 @include iro.props-store((
6 --dims: (
7 --diameter: fn.global-dim(--size --200),
8 --label-gap: fn.global-dim(--size --125),
9 --border: fn.global-dim(--border --medium),
10 --pad-i: fn.global-dim(--size --65),
11 --pad-b: fn.global-dim(--size --65),
12 --rounding: fn.global-dim(--rounding),
13 --spacing-sibling: fn.global-dim(--size --300),
14 ),
15 --colors: (
16 --circle-border: fn.global-color(--text-mute),
17 --circle-bg: fn.global-color(--base --75),
18
19 --hover: (
20 --label: fn.global-color(--heading),
21 --circle-border: fn.global-color(--text),
22 ),
23 --accent: (
24 --circle-border: fn.global-color(--accent --900),
25
26 --hover: (
27 --circle-border: fn.global-color(--accent --1000),
28 ),
29 ),
30 --key-focus: (
31 --bg: fn.global-color(--focus-static --400),
32 --label: fn.global-color(--focus-static --400-text),
33 --circle-border: fn.global-color(--focus-static --1000),
34 --circle-bg: fn.global-color(--focus-static --1000-text),
35 ),
36 --disabled: (
37 --label: fn.global-color(--text-disabled),
38 --circle-border: fn.global-color(--border-strong),
39 --circle-bg: fn.global-color(--base --75),
40 )
41 ),
42 ));
43
44 @include iro.bem-object(iro.props-namespace()) {
45 display: inline-flex;
46 position: relative;
47 align-items: flex-start;
48 margin-inline: calc(-1 * fn.dim(--pad-i)) calc(fn.dim(--spacing-sibling) - fn.dim(--pad-i));
49 padding-block: fn.dim(--pad-b);
50 padding-inline: fn.dim(--pad-i);
51
52 @include iro.bem-elem('bg') {
53 display: block;
54 position: absolute;
55 z-index: -1;
56 inset-block: 0;
57 inset-inline: 0;
58 border-radius: fn.dim(--rounding);
59 pointer-events: none;
60 }
61
62 @include iro.bem-elem('circle') {
63 display: block;
64 box-sizing: border-box;
65 flex: 0 0 auto;
66 inline-size: fn.dim(--diameter);
67 block-size: fn.dim(--diameter);
68 margin-block-start: calc(.5 * (fn.global-dim(--font --standard --line-height) * 1em - fn.dim(--diameter)));
69 border-radius: 2em;
70 background-color: fn.color(--circle-border);
71
72 &::after {
73 content: '';
74 display: block;
75 position: relative;
76 inset-block-start: fn.dim(--border);
77 inset-inline-start: fn.dim(--border);
78 inline-size: calc(fn.dim(--diameter) - 2 * fn.dim(--border));
79 block-size: calc(fn.dim(--diameter) - 2 * fn.dim(--border));
80 transition: transform .2s ease;
81 border-radius: fn.dim(--diameter);
82 background-color: fn.color(--circle-bg);
83 }
84 }
85
86 @include iro.bem-elem('label') {
87 align-self: baseline;
88 margin-inline-start: fn.dim(--label-gap);
89 }
90
91 @include iro.bem-elem('native') {
92 position: absolute;
93 inset-block-start: 0;
94 inset-inline-start: 0;
95 inline-size: 100%;
96 block-size: 100%;
97 margin: 0;
98 padding: 0;
99 overflow: hidden;
100 opacity: .0001;
101
102 &:hover {
103 @include iro.bem-sibling-elem('label') {
104 color: fn.color(--hover --label);
105 }
106
107 @include iro.bem-sibling-elem('circle') {
108 background-color: fn.color(--hover --circle-border);
109 }
110 }
111
112 &:checked {
113 @include iro.bem-sibling-elem('circle') {
114 &::after {
115 transform: scale(.44);
116 }
117 }
118 }
119
120 &:disabled {
121 @include iro.bem-sibling-elem('label') {
122 color: fn.color(--disabled --label);
123 }
124
125 @include iro.bem-sibling-elem('circle') {
126 background-color: fn.color(--disabled --circle-border);
127
128 &::after {
129 background-color: fn.color(--disabled --circle-bg);
130 }
131 }
132 }
133
134 &:focus-visible {
135 @include iro.bem-sibling-elem('bg') {
136 background-color: fn.color(--key-focus --bg);
137 }
138
139 @include iro.bem-sibling-elem('label') {
140 color: fn.color(--key-focus --label);
141 }
142
143 @include iro.bem-sibling-elem('circle') {
144 background-color: fn.color(--key-focus --circle-border);
145
146 &::after {
147 background-color: fn.color(--key-focus --circle-bg);
148 }
149 }
150 }
151 }
152
153 @include iro.bem-modifier('standalone') {
154 @include iro.bem-elem('circle') {
155 margin-block-start: 0;
156 }
157 }
158
159 @include iro.bem-modifier('accent') {
160 @include iro.bem-elem('native') {
161 &:checked {
162 @include iro.bem-sibling-elem('circle') {
163 background-color: fn.color(--accent --circle-border);
164 }
165
166 &:hover {
167 @include iro.bem-sibling-elem('circle') {
168 background-color: fn.color(--accent --hover --circle-border);
169 }
170 }
171 }
172
173 &:disabled {
174 @include iro.bem-sibling-elem('circle') {
175 background-color: fn.color(--disabled --circle-border);
176
177 &::after {
178 background-color: fn.color(--disabled --circle-bg);
179 }
180 }
181
182 &:checked {
183 @include iro.bem-sibling-elem('circle') {
184 background-color: fn.color(--disabled --circle-border);
185 }
186 }
187 }
188
189 &:focus-visible {
190 @include iro.bem-sibling-elem('circle') {
191 background-color: fn.color(--key-focus --circle-border);
192 }
193 }
194 }
195 }
196 }
197}
diff --git a/src/objects/_text-field.scss b/src/objects/_text-field.scss
index de5bcd1..04c809e 100644
--- a/src/objects/_text-field.scss
+++ b/src/objects/_text-field.scss
@@ -26,7 +26,8 @@
26 26
27@mixin keyboard-focus { 27@mixin keyboard-focus {
28 @include iro.bem-sibling-elem('bg') { 28 @include iro.bem-sibling-elem('bg') {
29 border: fn.dim(--key-focus --border) solid fn.color(--key-focus --border); 29 border-color: fn.color(--key-focus --border);
30 box-shadow: 0 0 0 fn.dim(--key-focus --border) fn.color(--key-focus --outline);
30 } 31 }
31} 32}
32 33
@@ -46,9 +47,10 @@
46 --focus: ( 47 --focus: (
47 --border: fn.global-dim(--border --medium), 48 --border: fn.global-dim(--border --medium),
48 ), 49 ),
50
49 --key-focus: ( 51 --key-focus: (
50 --border: fn.global-dim(--border --thick), 52 --border: fn.global-dim(--key-focus --border),
51 ) 53 ),
52 ), 54 ),
53 --colors: ( 55 --colors: (
54 --bg: fn.global-color(--base --50), 56 --bg: fn.global-color(--base --50),
@@ -63,7 +65,8 @@
63 --border: fn.global-color(--accent --900), 65 --border: fn.global-color(--accent --900),
64 ), 66 ),
65 --key-focus: ( 67 --key-focus: (
66 --border: fn.global-color(--yellow-static --400), 68 --border: fn.global-color(--focus --1300),
69 --outline: fn.global-color(--focus-static --400),
67 ), 70 ),
68 --error: ( 71 --error: (
69 --border: fn.global-color(--negative --900), 72 --border: fn.global-color(--negative --900),
diff --git a/src/scopes/_links.scss b/src/scopes/_links.scss
index a9de8b5..4f36972 100644
--- a/src/scopes/_links.scss
+++ b/src/scopes/_links.scss
@@ -33,8 +33,8 @@
33 ), 33 ),
34 34
35 --key-focus: ( 35 --key-focus: (
36 --bg: fn.global-color(--yellow-static --400), 36 --bg: fn.global-color(--focus-static --400),
37 --text: fn.global-color(--yellow-static --400-text), 37 --text: fn.global-color(--focus-static --400-text),
38 ) 38 )
39 ) 39 )
40 )); 40 ));
@@ -42,7 +42,9 @@
42 @include iro.bem-scope(iro.props-namespace()) { 42 @include iro.bem-scope(iro.props-namespace()) {
43 :link, 43 :link,
44 :visited { 44 :visited {
45 border-radius: fn.dim(--rounding); 45 margin: calc(-1 * fn.global-dim(--border --thick));
46 padding: fn.global-dim(--border --thick);
47 border-radius: calc(fn.dim(--rounding) + fn.global-dim(--border --thick));
46 color: currentColor; 48 color: currentColor;
47 text-decoration: underline; 49 text-decoration: underline;
48 text-decoration-color: fn.color(--underline); 50 text-decoration-color: fn.color(--underline);
@@ -56,7 +58,6 @@
56 58
57 &:focus-visible { 59 &:focus-visible {
58 background-color: fn.color(--key-focus --bg); 60 background-color: fn.color(--key-focus --bg);
59 box-shadow: 0 0 0 fn.global-dim(--border --thick) fn.color(--key-focus --bg);
60 color: fn.color(--key-focus --text); 61 color: fn.color(--key-focus --text);
61 text-decoration: underline; 62 text-decoration: underline;
62 text-decoration-thickness: fn.dim(--hover --underline); 63 text-decoration-thickness: fn.dim(--hover --underline);
diff --git a/tpl/objects/checkbox.pug b/tpl/objects/checkbox.pug
index 874505b..cb975d5 100644
--- a/tpl/objects/checkbox.pug
+++ b/tpl/objects/checkbox.pug
@@ -16,3 +16,4 @@ mixin checkbox
16 +icon('check')(class='o-checkbox__check-icon') 16 +icon('check')(class='o-checkbox__check-icon')
17 .o-checkbox__label 17 .o-checkbox__label
18 block 18 block
19 .o-checkbox__bg
diff --git a/tpl/objects/radio.pug b/tpl/objects/radio.pug
index 8cb1b1f..ba941be 100644
--- a/tpl/objects/radio.pug
+++ b/tpl/objects/radio.pug
@@ -10,3 +10,4 @@ mixin radio
10 .o-radio__circle 10 .o-radio__circle
11 .o-radio__label 11 .o-radio__label
12 block 12 block
13 .o-radio__bg
diff --git a/tpl/views/button.pug b/tpl/views/button.pug
index 4b2281e..7e8f572 100644
--- a/tpl/views/button.pug
+++ b/tpl/views/button.pug
@@ -46,7 +46,7 @@ mixin view-button
46 +a-button(outline=true icon='trash') 46 +a-button(outline=true icon='trash')
47 +a-button(outline=true disabled=true icon='trash') 47 +a-button(outline=true disabled=true icon='trash')
48 48
49 each theme in ['primary', 'accent', 'positive', 'negative', 'warning'] 49 each theme in ['primary', 'accent', 'negative']
50 .c-box 50 .c-box
51 .l-button-group 51 .l-button-group
52 +a-button(variant=theme)= 'Button' 52 +a-button(variant=theme)= 'Button'
diff --git a/tpl/views/links.pug b/tpl/views/links.pug
index 91d6dde..efc8817 100644
--- a/tpl/views/links.pug
+++ b/tpl/views/links.pug
@@ -5,7 +5,7 @@ mixin view-links
5 = 'Lorem ipsum dolor ' 5 = 'Lorem ipsum dolor '
6 a(href='https://mk.vulpes.one/')= 'https://mk.vulpes.one/' 6 a(href='https://mk.vulpes.one/')= 'https://mk.vulpes.one/'
7 = ' sit amet, consetetur ' 7 = ' sit amet, consetetur '
8 a(href='https://v.reddit.com/')= 'https://v.reddit.com/' 8 a(href='https://is-a.wyvern.rip/')= 'https://is-a.wyvern.rip/'
9 = ' sadipscing elitr, sed diam nonumy eirmod tempor' 9 = ' sadipscing elitr, sed diam nonumy eirmod tempor'
10 10
11 .c-box 11 .c-box
@@ -13,7 +13,7 @@ mixin view-links
13 = 'Lorem ipsum dolor ' 13 = 'Lorem ipsum dolor '
14 a(href='https://mk.vulpes.one/')= 'https://mk.vulpes.one/' 14 a(href='https://mk.vulpes.one/')= 'https://mk.vulpes.one/'
15 = ' sit amet, consetetur ' 15 = ' sit amet, consetetur '
16 a(href='https://v.reddit.com/')= 'https://v.reddit.com/' 16 a(href='https://is-a.wyvern.rip/')= 'https://is-a.wyvern.rip/'
17 = ' sadipscing elitr, sed diam nonumy eirmod tempor' 17 = ' sadipscing elitr, sed diam nonumy eirmod tempor'
18 18
19 .c-box 19 .c-box
@@ -21,5 +21,5 @@ mixin view-links
21 = 'Lorem ipsum dolor ' 21 = 'Lorem ipsum dolor '
22 a(href='https://mk.vulpes.one/')= 'https://mk.vulpes.one/' 22 a(href='https://mk.vulpes.one/')= 'https://mk.vulpes.one/'
23 = ' sit amet, consetetur ' 23 = ' sit amet, consetetur '
24 a(href='https://v.reddit.com/')= 'https://v.reddit.com/' 24 a(href='https://is-a.wyvern.rip/')= 'https://is-a.wyvern.rip/'
25 = ' sadipscing elitr, sed diam nonumy eirmod tempor' 25 = ' sadipscing elitr, sed diam nonumy eirmod tempor'