@oscarpalmer/atoms 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Oscar Palmér
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # Atoms
2
+
3
+ [![npm](https://badge.fury.io/js/@oscarpalmer%2Fatoms.svg)](https://www.npmjs.com/package/@oscarpalmer/atoms)
4
+
5
+ Sweet little atomic goodies…
6
+
7
+ ## License
8
+
9
+ [MIT licensed](LICENSE), natch :wink:
@@ -0,0 +1,14 @@
1
+ .sr-focusable, .sr-only {
2
+ position: absolute !important;
3
+ }
4
+ .sr-focusable:not(:focus):not(:focus-within), .sr-only {
5
+ width: 1px !important;
6
+ height: 1px !important;
7
+ padding: 0 !important;
8
+ margin: -1px !important;
9
+ clip: rect(1px, 1px 1px, 1px) !important;
10
+ clip-path: inset(50%) !important;
11
+ overflow: hidden !important;
12
+ border: 0 !important;
13
+ white-space: nowrap !important;
14
+ }
@@ -0,0 +1,148 @@
1
+ .fill {
2
+ flex: 1;
3
+ }
4
+
5
+ .flow,
6
+ .stack {
7
+ display: flex;
8
+ }
9
+
10
+ .flow {
11
+ flex-flow: row wrap;
12
+ gap: var(--flow-gap, 1rem);
13
+ align-items: center;
14
+ }
15
+ .flow-large {
16
+ gap: calc(2 * var(--flow-gap, 1rem));
17
+ }
18
+ .flow-nowrap {
19
+ flex-wrap: nowrap;
20
+ }
21
+ .flow-reverse {
22
+ flex-flow: row-reverse wrap-reverse;
23
+ }
24
+ .flow-small {
25
+ gap: calc(0.5 * var(--flow-gap, 1rem));
26
+ }
27
+ .flow-nogap {
28
+ gap: 0;
29
+ }
30
+
31
+ .stack {
32
+ flex-flow: column nowrap;
33
+ gap: var(--stack-gap, 1rem);
34
+ }
35
+ .stack-large {
36
+ gap: calc(2 * var(--stack-gap, 1rem));
37
+ }
38
+ .stack-reverse {
39
+ flex-flow: column-reverse nowrap;
40
+ }
41
+ .stack-small {
42
+ gap: calc(0.5 * var(--stack-gap, 1rem));
43
+ }
44
+ .stack-nogap {
45
+ gap: 0;
46
+ }
47
+
48
+ .ac-c {
49
+ align-content: center;
50
+ }
51
+ .ac-fe {
52
+ align-content: flex-end;
53
+ }
54
+ .ac-fs {
55
+ align-content: flex-start;
56
+ }
57
+ .ac-s {
58
+ align-content: stretch;
59
+ }
60
+
61
+ .ai-c {
62
+ align-items: center;
63
+ }
64
+ .ai-fe {
65
+ align-items: flex-end;
66
+ }
67
+ .ai-fs {
68
+ align-items: flex-start;
69
+ }
70
+ .ai-s {
71
+ align-items: stretch;
72
+ }
73
+
74
+ .as-c {
75
+ align-self: center;
76
+ }
77
+ .as-fe {
78
+ align-self: flex-end;
79
+ }
80
+ .as-fs {
81
+ align-self: flex-start;
82
+ }
83
+ .as-s {
84
+ align-self: stretch;
85
+ }
86
+
87
+ .ac-sa {
88
+ align-content: space-around;
89
+ }
90
+
91
+ .ac-sb {
92
+ align-content: space-between;
93
+ }
94
+
95
+ .ac-se {
96
+ align-content: space-evenly;
97
+ }
98
+
99
+ .jc-c {
100
+ justify-content: center;
101
+ }
102
+ .jc-fe {
103
+ justify-content: flex-end;
104
+ }
105
+ .jc-fs {
106
+ justify-content: flex-start;
107
+ }
108
+ .jc-s {
109
+ justify-content: stretch;
110
+ }
111
+
112
+ .ji-c {
113
+ justify-items: center;
114
+ }
115
+ .ji-fe {
116
+ justify-items: flex-end;
117
+ }
118
+ .ji-fs {
119
+ justify-items: flex-start;
120
+ }
121
+ .ji-s {
122
+ justify-items: stretch;
123
+ }
124
+
125
+ .js-c {
126
+ justify-self: center;
127
+ }
128
+ .js-fe {
129
+ justify-self: flex-end;
130
+ }
131
+ .js-fs {
132
+ justify-self: flex-start;
133
+ }
134
+ .js-s {
135
+ justify-self: stretch;
136
+ }
137
+
138
+ .jc-sa {
139
+ justify-content: space-around;
140
+ }
141
+
142
+ .jc-sb {
143
+ justify-content: space-between;
144
+ }
145
+
146
+ .jc-se {
147
+ justify-content: space-evenly;
148
+ }
@@ -0,0 +1,313 @@
1
+ @charset "UTF-8";
2
+ /*! https://github.com/oscarpalmer/atoms */
3
+ /**
4
+ * Reset
5
+ * ========================
6
+ *
7
+ * A reset I use for a lot of personal stuff, and elsewhere, of course.
8
+ * It's a mix of a few different resets mixed together with some of my own ideas.
9
+ *
10
+ * When rules are explained, such comments will be suffixed with one ore more square brackets
11
+ * with a two-letter code, all of which are listed below. My own code is "OP", which are my initials.
12
+ *
13
+ * - Andy Bell's "A (more) Modern CSS Reset" [AB]
14
+ * https://andy-bell.co.uk/a-more-modern-css-reset/
15
+ *
16
+ * - CSS Remedy [CR]
17
+ * https://github.com/jensimmons/cssremedy
18
+ *
19
+ * - RESS [RS]
20
+ * https://github.com/filipelinhares/ress
21
+ *
22
+ * - Sanitize.css [SE]
23
+ * https://github.com/csstools/sanitize.css
24
+ */
25
+ /**
26
+ * Catch-alls
27
+ */
28
+ * {
29
+ padding: 0;
30
+ margin: 0;
31
+ box-sizing: border-box;
32
+ background-repeat: no-repeat;
33
+ }
34
+
35
+ ::before,
36
+ ::after {
37
+ box-sizing: inherit;
38
+ text-decoration: inherit;
39
+ vertical-align: inherit;
40
+ }
41
+
42
+ /**
43
+ * Document
44
+ */
45
+ :where(:root) {
46
+ font-family: system-ui, sans-serif;
47
+ font-size: 100%;
48
+ line-height: 1.5;
49
+ -moz-text-size-adjust: none;
50
+ -webkit-text-size-adjust: none;
51
+ text-size-adjust: none;
52
+ -moz-tab-size: 4;
53
+ tab-size: 4;
54
+ -webkit-tap-highlight-color: transparent;
55
+ }
56
+
57
+ :where(body) {
58
+ min-height: 100vh;
59
+ font-size: 1rem;
60
+ }
61
+
62
+ /**
63
+ * General
64
+ */
65
+ :where(a) {
66
+ background-color: transparent;
67
+ }
68
+
69
+ :where(abbr[title]) {
70
+ border-bottom: none;
71
+ text-decoration: underline;
72
+ text-decoration: underline dotted;
73
+ }
74
+
75
+ :where(b, strong) {
76
+ font-weight: bolder;
77
+ }
78
+
79
+ :where(button, [type=button i], [type=reset i], [type=submit i]) {
80
+ -webkit-appearance: button;
81
+ cursor: pointer;
82
+ }
83
+
84
+ :where(button, select) {
85
+ text-transform: none;
86
+ }
87
+
88
+ :where(button) {
89
+ overflow: visible;
90
+ }
91
+
92
+ :where(code, kbd, pre, samp) {
93
+ font-family: monospace, monospace;
94
+ font-size: 1em;
95
+ }
96
+
97
+ :where(details > summary:first-of-type) {
98
+ display: list-item;
99
+ }
100
+
101
+ :where(dl, ol, ul) :where(dl, ol, ul) {
102
+ margin: 0;
103
+ }
104
+
105
+ :where(h1, h2, h3, h4, h5, h6, label, legend) {
106
+ line-height: 1.125;
107
+ }
108
+
109
+ :where(h1, h2, h3, h4, h5, h6) {
110
+ text-wrap: balance;
111
+ }
112
+
113
+ :where(hr) {
114
+ height: 0;
115
+ overflow: visible;
116
+ border-style: solid;
117
+ border-width: 1px 0 0;
118
+ color: inherit;
119
+ }
120
+
121
+ :where(nav) :where(ol, ul) {
122
+ list-style-type: none;
123
+ padding: 0;
124
+ }
125
+
126
+ :where(nav li)::before {
127
+ content: "​";
128
+ float: left;
129
+ }
130
+
131
+ :where(pre) {
132
+ overflow: auto;
133
+ white-space: pre-wrap;
134
+ }
135
+
136
+ :where(small) {
137
+ font-size: 0.875rem;
138
+ }
139
+
140
+ :where(sub, sup) {
141
+ vertical-align: baseline;
142
+ font-size: 0.75rem;
143
+ line-height: 0;
144
+ position: relative;
145
+ }
146
+
147
+ :where(sub) {
148
+ bottom: -0.25em;
149
+ }
150
+
151
+ :where(sup) {
152
+ top: -0.5em;
153
+ }
154
+
155
+ :where(table) {
156
+ border-collapse: collapse;
157
+ border-color: currentColor;
158
+ text-indent: 0;
159
+ }
160
+
161
+ /**
162
+ * Form-related
163
+ */
164
+ :where(button, input, optgroup, select, textarea) {
165
+ font: inherit;
166
+ }
167
+
168
+ :where(button, input, select, textarea) {
169
+ padding: 0.25em 0.5em;
170
+ background-color: transparent;
171
+ border: 1px solid WindowFrame;
172
+ color: inherit;
173
+ letter-spacing: inherit;
174
+ }
175
+
176
+ :where(button, input) {
177
+ line-height: 1.25;
178
+ }
179
+
180
+ :where(fieldset) {
181
+ border: 1px solid darkgrey;
182
+ }
183
+
184
+ :where(optgroup) {
185
+ font-weight: bold;
186
+ }
187
+
188
+ :where(progress) {
189
+ vertical-align: baseline;
190
+ }
191
+
192
+ :where(select) {
193
+ -webkit-appearance: none;
194
+ appearance: none;
195
+ padding-right: 1em;
196
+ background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='4'%3E%3Cpath d='M4 0h6L7 4'/%3E%3C/svg%3E") no-repeat right center/1em;
197
+ border-radius: 0;
198
+ }
199
+
200
+ :where(select[multiple]) {
201
+ background-image: none;
202
+ }
203
+
204
+ select::-ms-expand {
205
+ display: none;
206
+ }
207
+
208
+ select::-ms-value {
209
+ color: currentColor;
210
+ }
211
+
212
+ :where(textarea) {
213
+ overflow: auto;
214
+ resize: vertical;
215
+ }
216
+
217
+ :where([type=color i], [type=range i]) {
218
+ border-width: 0;
219
+ padding: 0;
220
+ }
221
+
222
+ :where([type=search i]) {
223
+ -webkit-appearance: textfield;
224
+ outline-offset: -2px;
225
+ }
226
+
227
+ ::-webkit-file-upload-button {
228
+ -webkit-appearance: button;
229
+ color: inherit;
230
+ font: inherit;
231
+ line-height: 1.25;
232
+ }
233
+
234
+ ::-webkit-inner-spin-button,
235
+ ::-webkit-outer-spin-button {
236
+ height: auto;
237
+ }
238
+
239
+ ::-webkit-input-placeholder {
240
+ color: inherit;
241
+ opacity: 0.54;
242
+ }
243
+
244
+ ::-webkit-search-decoration {
245
+ -webkit-appearance: none;
246
+ }
247
+
248
+ /**
249
+ * Media-related
250
+ */
251
+ :where(audio, canvas, embed, iframe, img, object, picture, svg, video) {
252
+ max-width: 100%;
253
+ vertical-align: middle;
254
+ }
255
+
256
+ :where(audio, canvas, embed, iframe, img, object, svg, video) {
257
+ display: block;
258
+ }
259
+
260
+ :where(audio) {
261
+ width: 100%;
262
+ }
263
+
264
+ :where(audio:not([controls])) {
265
+ display: none;
266
+ }
267
+
268
+ :where(canvas, img, svg, video) {
269
+ height: auto;
270
+ }
271
+
272
+ :where(iframe) {
273
+ border-style: none;
274
+ }
275
+
276
+ :where(img) {
277
+ border-style: none;
278
+ }
279
+
280
+ :where(picture) {
281
+ display: contents;
282
+ }
283
+
284
+ :where(source) {
285
+ display: none;
286
+ }
287
+
288
+ :where(svg) {
289
+ overflow: hidden;
290
+ }
291
+
292
+ :where(svg:not([fill])) {
293
+ fill: currentColor;
294
+ }
295
+
296
+ /**
297
+ * Attributes
298
+ */
299
+ :where([disabled], [aria-disabled=true i]) {
300
+ cursor: default;
301
+ }
302
+
303
+ [hidden] {
304
+ display: none !important;
305
+ }
306
+
307
+ :where([aria-busy=true i]) {
308
+ cursor: progress;
309
+ }
310
+
311
+ :where([aria-controls]) {
312
+ cursor: pointer;
313
+ }
@@ -0,0 +1,36 @@
1
+ // src/js/value.ts
2
+ function getValue(data, key) {
3
+ if (typeof data !== "object" || data === null || isNullableOrWhitespace(key)) {
4
+ return;
5
+ }
6
+ const parts = getString(key).split(".");
7
+ const length = parts.length;
8
+ let index = 0;
9
+ let value = data;
10
+ while (typeof value === "object" && value !== null && index < length) {
11
+ value = value[parts[index++]];
12
+ }
13
+ return value;
14
+ }
15
+ function isNullable(value) {
16
+ return value === undefined || value === null;
17
+ }
18
+
19
+ // src/js/string.ts
20
+ function createUuid() {
21
+ return uuidTemplate.replace(/[018]/g, (substring) => (substring ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> substring / 4).toString(16));
22
+ }
23
+ function getString(value2) {
24
+ return typeof value2 === "string" ? value2 : String(value2);
25
+ }
26
+ function isNullableOrWhitespace(value2) {
27
+ return isNullable(value2) || getString(value2).trim().length === 0;
28
+ }
29
+ var uuidTemplate = "10000000-1000-4000-8000-100000000000";
30
+ export {
31
+ isNullableOrWhitespace,
32
+ isNullable,
33
+ getValue,
34
+ getString,
35
+ createUuid
36
+ };
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "author": {
3
+ "name": "Oscar Palmér",
4
+ "url": "https://oscarpalmer.se"
5
+ },
6
+ "description": "Sweet little atomic goodies…",
7
+ "devDependencies": {
8
+ "@rollup/plugin-typescript": "^11.1",
9
+ "bun": "^1.0",
10
+ "prettier": "^3.1",
11
+ "rollup": "^4.5",
12
+ "sass": "^1.69",
13
+ "typescript": "^5.3",
14
+ "xo": "^0.56"
15
+ },
16
+ "exports": {
17
+ ".": {
18
+ "types": "./types/index.d.ts",
19
+ "import": "./dist/index.js"
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "src",
26
+ "types"
27
+ ],
28
+ "keywords": [],
29
+ "license": "MIT",
30
+ "main": "./dist/js/index.js",
31
+ "module": "./dist/js/index.js",
32
+ "name": "@oscarpalmer/atoms",
33
+ "prettier": {
34
+ "arrowParens": "avoid",
35
+ "bracketSpacing": false,
36
+ "singleQuote": true,
37
+ "switchIndent": true,
38
+ "trailingComma": "all",
39
+ "useTabs": true
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/oscarpalmer/atoms.git"
44
+ },
45
+ "scripts": {
46
+ "build": "npm run build:css && npm run build:js && npm run types",
47
+ "build:css": "npx sass ./src/css:./dist/css --no-source-map",
48
+ "build:js": "npx bun build ./src/js/index.ts --outfile ./dist/js/atoms.js",
49
+ "test": "npx bun test --coverage",
50
+ "types": "npx tsc ./src/js/index.ts --declaration --declarationDir ./types --emitDeclarationOnly",
51
+ "watch:css": "npx sass ./src/css:./dist/css --no-source-map --watch",
52
+ "watch:js": "npx bun build ./src/js/index.ts --outfile ./dist/js/atoms.js --watch"
53
+ },
54
+ "type": "module",
55
+ "types": "./types/index.d.ts",
56
+ "version": "0.5.0",
57
+ "xo": {
58
+ "envs": [
59
+ "browser"
60
+ ],
61
+ "prettier": true,
62
+ "rules": {
63
+ "import/extensions": "off",
64
+ "import/no-cycle": "off"
65
+ }
66
+ }
67
+ }
@@ -0,0 +1,19 @@
1
+ .sr {
2
+ &-focusable,
3
+ &-only {
4
+ position: absolute !important;
5
+ }
6
+
7
+ &-focusable:not(:focus):not(:focus-within),
8
+ &-only {
9
+ width: 1px !important;
10
+ height: 1px !important;
11
+ padding: 0 !important;
12
+ margin: -1px !important;
13
+ clip: rect(1px, 1px 1px, 1px) !important;
14
+ clip-path: inset(50%) !important;
15
+ overflow: hidden !important;
16
+ border: 0 !important;
17
+ white-space: nowrap !important;
18
+ }
19
+ }
@@ -0,0 +1,78 @@
1
+ .fill {
2
+ flex: 1;
3
+ }
4
+
5
+ .flow,
6
+ .stack {
7
+ display: flex;
8
+ }
9
+
10
+ .flow {
11
+ flex-flow: row wrap;
12
+ gap: var(--flow-gap, 1rem);
13
+ align-items: center;
14
+
15
+ &-large {
16
+ gap: calc(2 * var(--flow-gap, 1rem));
17
+ }
18
+
19
+ &-nowrap {
20
+ flex-wrap: nowrap;
21
+ }
22
+
23
+ &-reverse {
24
+ flex-flow: row-reverse wrap-reverse;
25
+ }
26
+
27
+ &-small {
28
+ gap: calc(0.5 * var(--flow-gap, 1rem));
29
+ }
30
+
31
+ &-nogap {
32
+ gap: 0;
33
+ }
34
+ }
35
+
36
+ .stack {
37
+ flex-flow: column nowrap;
38
+ gap: var(--stack-gap, 1rem);
39
+
40
+ &-large {
41
+ gap: calc(2 * var(--stack-gap, 1rem));
42
+ }
43
+
44
+ &-reverse {
45
+ flex-flow: column-reverse nowrap;
46
+ }
47
+
48
+ &-small {
49
+ gap: calc(0.5 * var(--stack-gap, 1rem));
50
+ }
51
+
52
+ &-nogap {
53
+ gap: 0;
54
+ }
55
+ }
56
+
57
+ $alignmentOrigins: (c: 'content', i: 'items', s: 'self');
58
+ $alignmentValues: (c: 'center', fe: 'flex-end', fs: 'flex-start', s: 'stretch');
59
+ $justificationValues: (sa: 'space-around', sb: 'space-between', se: 'space-evenly');
60
+ $types: (a: 'align', j: 'justify');
61
+
62
+ @each $typeKey, $typeValue in $types {
63
+ @each $alignmentOriginKey, $alignmentOriginValue in $alignmentOrigins {
64
+ .#{$typeKey}#{$alignmentOriginKey} {
65
+ @each $alignmentValueKey, $alignmentValueValue in $alignmentValues {
66
+ &-#{$alignmentValueKey} {
67
+ #{$typeValue}-#{$alignmentOriginValue}: #{$alignmentValueValue}
68
+ }
69
+ }
70
+ }
71
+ }
72
+
73
+ @each $justificationValueKey, $justificationValueValue in $justificationValues {
74
+ .#{$typeKey}c-#{$justificationValueKey} {
75
+ #{$typeValue}-content: #{$justificationValueValue}
76
+ }
77
+ }
78
+ }
@@ -0,0 +1,320 @@
1
+ /*! https://github.com/oscarpalmer/atoms */
2
+ /**
3
+ * Reset
4
+ * ========================
5
+ *
6
+ * A reset I use for a lot of personal stuff, and elsewhere, of course.
7
+ * It's a mix of a few different resets mixed together with some of my own ideas.
8
+ *
9
+ * When rules are explained, such comments will be suffixed with one ore more square brackets
10
+ * with a two-letter code, all of which are listed below. My own code is "OP", which are my initials.
11
+ *
12
+ * - Andy Bell's "A (more) Modern CSS Reset" [AB]
13
+ * https://andy-bell.co.uk/a-more-modern-css-reset/
14
+ *
15
+ * - CSS Remedy [CR]
16
+ * https://github.com/jensimmons/cssremedy
17
+ *
18
+ * - RESS [RS]
19
+ * https://github.com/filipelinhares/ress
20
+ *
21
+ * - Sanitize.css [SE]
22
+ * https://github.com/csstools/sanitize.css
23
+ */
24
+
25
+ /**
26
+ * Catch-alls
27
+ */
28
+
29
+ * {
30
+ padding: 0;
31
+ margin: 0;
32
+ box-sizing: border-box;
33
+ background-repeat: no-repeat;
34
+ }
35
+
36
+ ::before,
37
+ ::after {
38
+ box-sizing: inherit;
39
+ text-decoration: inherit;
40
+ vertical-align: inherit;
41
+ }
42
+
43
+ /**
44
+ * Document
45
+ */
46
+
47
+ :where(:root) {
48
+ font-family: system-ui, sans-serif;
49
+ font-size: 100%;
50
+ line-height: 1.5;
51
+ -moz-text-size-adjust: none; // Prevent text size inflation (e.g., after orientation change) [AB][RE][SE]
52
+ -webkit-text-size-adjust: none; // ditto
53
+ text-size-adjust: none; // ditto
54
+ -moz-tab-size: 4;
55
+ tab-size: 4;
56
+ -webkit-tap-highlight-color: transparent; // Remove the gray highlight on active links in IE 10 [SE]
57
+ }
58
+
59
+ :where(body) {
60
+ min-height: 100vh;
61
+ font-size: 1rem;
62
+ }
63
+
64
+ /**
65
+ * General
66
+ */
67
+
68
+ :where(a) {
69
+ background-color: transparent; // Remove the gray background on active links in IE10 [RS]
70
+ }
71
+
72
+ :where(abbr[title]) {
73
+ border-bottom: none; // Remove the bottom border in Chrome 57 [RS]
74
+ text-decoration: underline; // Add the correct text decoration in Safari [RS][SE]
75
+ text-decoration: underline dotted; // ditto
76
+ }
77
+
78
+ :where(b, strong) {
79
+ font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari [RS][SE]
80
+ }
81
+
82
+ :where(button, [type='button' i], [type='reset' i], [type='submit' i]) {
83
+ -webkit-appearance: button; // Correct the inability to style clickable types in iOS [RS][SE]
84
+ cursor: pointer;
85
+ }
86
+
87
+ :where(button, select) {
88
+ text-transform: none; // Undo transformation in Firefox 40+ and IE11 [RS]
89
+ }
90
+
91
+ :where(button) {
92
+ overflow: visible; // Address 'overflow' set to 'hidden' in IE8–11 [RS]
93
+ }
94
+
95
+ :where(code, kbd, pre, samp) {
96
+ font-family: monospace, monospace; // Specify the font family of code elements [RS][SE]
97
+ font-size: 1em; // Correct the odd 'em' font sizing in all browsers [RS][SE]
98
+ }
99
+
100
+ :where(details > summary:first-of-type) {
101
+ display: list-item; // Add the correct display in Safari [SE]
102
+ }
103
+
104
+ :where(dl, ol, ul) :where(dl, ol, ul) {
105
+ margin: 0; // Remove the margin on nested lists in Chrome, Edge, and Safari [SE]
106
+ }
107
+
108
+ :where(h1, h2, h3, h4, h5, h6, label, legend) {
109
+ line-height: 1.125;
110
+ }
111
+
112
+ :where(h1, h2, h3, h4, h5, h6) {
113
+ text-wrap: balance;
114
+ }
115
+
116
+ :where(hr) {
117
+ height: 0; // Add the correct box sizing in Firefox [CR][RS][SE]
118
+ overflow: visible; // Show the overflow in Edge and IE [CR][RS]
119
+ border-style: solid;
120
+ border-width: 1px 0 0;
121
+ color: inherit; // Correct the inheritance of border colour in Firefox [CR][RS][SE]
122
+ }
123
+
124
+ :where(nav) :where(ol, ul) {
125
+ list-style-type: none;
126
+ padding: 0;
127
+ }
128
+
129
+ :where(nav li)::before {
130
+ content: '\200B'; // Prevent VoiceOver from ignoring list semantics in Safari [SE]
131
+ float: left;
132
+ }
133
+
134
+ :where(pre) {
135
+ overflow: auto; // Prevent overflow of the container in all browsers [SE]
136
+ white-space: pre-wrap; // Overflow by default is bad… [CR]
137
+ }
138
+
139
+ :where(small) {
140
+ font-size: 0.875rem;
141
+ }
142
+
143
+ :where(sub, sup) {
144
+ vertical-align: baseline;
145
+ font-size: 0.75rem;
146
+ line-height: 0;
147
+ position: relative;
148
+ }
149
+
150
+ :where(sub) {
151
+ bottom: -0.25em;
152
+ }
153
+
154
+ :where(sup) {
155
+ top: -0.5em;
156
+ }
157
+
158
+ :where(table) {
159
+ border-collapse: collapse;
160
+ border-color: currentColor; // Correct table border colour in Chrome, Edge, and Safari [SE]
161
+ text-indent: 0; // Remove text indentation from table contents in Chrome, Edge, and Safari [RS][SE]
162
+ }
163
+
164
+ /**
165
+ * Form-related
166
+ */
167
+
168
+ :where(button, input, optgroup, select, textarea) {
169
+ font: inherit; // Correct the font inheritance in Firefox, Edge, and Safari [RS][SE]
170
+ }
171
+
172
+ :where(button, input, select, textarea) {
173
+ padding: 0.25em 0.5em; // Change the inconsistent appearance in all browsers [SE]
174
+ background-color: transparent; // ditto
175
+ border: 1px solid WindowFrame; // ditto
176
+ color: inherit; // ditto
177
+ letter-spacing: inherit; // ditto
178
+ }
179
+
180
+ :where(button, input) {
181
+ line-height: 1.25;
182
+ }
183
+
184
+ :where(fieldset) {
185
+ border: 1px solid darkgrey; // Change the inconsistent appearance in all browsers [SE]
186
+ }
187
+
188
+ :where(optgroup) {
189
+ font-weight: bold; // Restore the font weight unset by previous rules [RS]
190
+ }
191
+
192
+ :where(progress) {
193
+ vertical-align: baseline; // Add the correct vertical alignment in Chrome, Edge, and Firefox [RS][SE]
194
+ }
195
+
196
+ :where(select) {
197
+ -webkit-appearance: none; // Change the inconsistent appearance in all browsers [SE]
198
+ appearance: none; // ditto
199
+ padding-right: 1em; // ditto
200
+ background: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='4'%3E%3Cpath d='M4 0h6L7 4'/%3E%3C/svg%3E")
201
+ no-repeat right center / 1em; // ditto
202
+ border-radius: 0; // ditto
203
+ }
204
+
205
+ :where(select[multiple]) {
206
+ background-image: none; // Don't show the arrow for multiple choice selects [SE]
207
+ }
208
+
209
+ select::-ms-expand {
210
+ display: none; // Hide in IE11+ [RS]
211
+ }
212
+
213
+ select::-ms-value {
214
+ color: currentColor; // Set text colour in IE11+ [RS]
215
+ }
216
+
217
+ :where(textarea) {
218
+ overflow: auto; // Allow overflow in IE11+ [RS]
219
+ resize: vertical; // Change the resize direction in all browsers [RS][SE]
220
+ }
221
+
222
+ :where([type='color' i], [type='range' i]) {
223
+ border-width: 0; // Remove the border and padding in all browsers [SE]
224
+ padding: 0; // ditto
225
+ }
226
+
227
+ :where([type='search' i]) {
228
+ -webkit-appearance: textfield; // Correct the odd appearance in Chrome, Edge, and Safari [RS][SE]
229
+ outline-offset: -2px; // Correct the outline style in Safari [RS][SE]
230
+ }
231
+
232
+ ::-webkit-file-upload-button {
233
+ -webkit-appearance: button; // Correct the inability to style upload buttons in iOS and Safari [RS][SE]
234
+ color: inherit;
235
+ font: inherit;
236
+ line-height: 1.25;
237
+ }
238
+
239
+ ::-webkit-inner-spin-button,
240
+ ::-webkit-outer-spin-button {
241
+ height: auto; // Correct the cursor style of increment and decrement buttons in Safari [RS][SE]
242
+ }
243
+
244
+ ::-webkit-input-placeholder {
245
+ color: inherit; // Correct the text style of placeholders in Chrome, Edge, and Safari. [SE]
246
+ opacity: 0.54; // ditto
247
+ }
248
+
249
+ ::-webkit-search-decoration {
250
+ -webkit-appearance: none; // Remove the inner padding in Chrome, Edge, and Safari on macOS [SE]
251
+ }
252
+
253
+ /**
254
+ * Media-related
255
+ */
256
+
257
+ :where(audio, canvas, embed, iframe, img, object, picture, svg, video) {
258
+ max-width: 100%;
259
+ vertical-align: middle;
260
+ }
261
+
262
+ :where(audio, canvas, embed, iframe, img, object, svg, video) {
263
+ display: block;
264
+ }
265
+
266
+ :where(audio) {
267
+ width: 100%; // Better default width [CR]
268
+ }
269
+
270
+ :where(audio:not([controls])) {
271
+ display: none;
272
+ }
273
+
274
+ :where(canvas, img, svg, video) {
275
+ height: auto; // Maintain aspect ratios [CR]
276
+ }
277
+
278
+ :where(iframe) {
279
+ border-style: none;
280
+ }
281
+
282
+ :where(img) {
283
+ border-style: none; // Remove the border on images inside links in IE 10 and earlier [CR]
284
+ }
285
+
286
+ :where(picture) {
287
+ display: contents; // Remove the unnecessary wrapping 'picture', while maintaining contents [CR]
288
+ }
289
+
290
+ :where(source) {
291
+ display: none; // Source elements have nothing to display, so we hide them entirely [CR]
292
+ }
293
+
294
+ :where(svg) {
295
+ overflow: hidden; // Hide the overflow in IE 10 and earlier [CR]
296
+ }
297
+
298
+ :where(svg:not([fill])) {
299
+ fill: currentColor;
300
+ }
301
+
302
+ /**
303
+ * Attributes
304
+ */
305
+
306
+ :where([disabled], [aria-disabled='true' i]) {
307
+ cursor: default;
308
+ }
309
+
310
+ [hidden] {
311
+ display: none !important;
312
+ }
313
+
314
+ :where([aria-busy='true' i]) {
315
+ cursor: progress;
316
+ }
317
+
318
+ :where([aria-controls]) {
319
+ cursor: pointer;
320
+ }
@@ -0,0 +1,2 @@
1
+ export * from './string';
2
+ export * from './value';
@@ -0,0 +1,31 @@
1
+ import {isNullable} from './value';
2
+
3
+ const uuidTemplate = '10000000-1000-4000-8000-100000000000';
4
+
5
+ /**
6
+ * Create a new UUID
7
+ */
8
+ export function createUuid(): string {
9
+ return uuidTemplate.replace(/[018]/g, (substring: any) =>
10
+ (
11
+ substring ^
12
+ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (substring / 4)))
13
+ ).toString(16),
14
+ );
15
+ }
16
+
17
+ /**
18
+ * Get the string value from any value
19
+ */
20
+ export function getString(value: unknown): string {
21
+ return typeof value === 'string' ? value : String(value);
22
+ }
23
+
24
+ /**
25
+ * Is the value undefined, null, or an empty string?
26
+ */
27
+ export function isNullableOrWhitespace(
28
+ value: unknown,
29
+ ): value is undefined | null | '' {
30
+ return isNullable(value) || getString(value).trim().length === 0;
31
+ }
@@ -0,0 +1,34 @@
1
+ import {getString, isNullableOrWhitespace} from './string';
2
+
3
+ /**
4
+ * - Get the value from an object using a path
5
+ * - You can retrieve a nested value by using a dot notation path
6
+ */
7
+ export function getValue(data: unknown, key: unknown): unknown {
8
+ if (
9
+ typeof data !== 'object' ||
10
+ data === null ||
11
+ isNullableOrWhitespace(key)
12
+ ) {
13
+ return undefined;
14
+ }
15
+
16
+ const parts = getString(key).split('.');
17
+ const length = parts.length;
18
+
19
+ let index = 0;
20
+ let value = data;
21
+
22
+ while (typeof value === 'object' && value !== null && index < length) {
23
+ value = value[parts[index++]];
24
+ }
25
+
26
+ return value;
27
+ }
28
+
29
+ /**
30
+ * Is the value undefined or null?
31
+ */
32
+ export function isNullable(value: unknown): value is undefined | null {
33
+ return value === undefined || value === null;
34
+ }
@@ -0,0 +1,2 @@
1
+ export * from './string';
2
+ export * from './value';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Create a new UUID
3
+ */
4
+ export declare function createUuid(): string;
5
+ /**
6
+ * Get the string value from any value
7
+ */
8
+ export declare function getString(value: unknown): string;
9
+ /**
10
+ * Is the value undefined, null, or an empty string?
11
+ */
12
+ export declare function isNullableOrWhitespace(value: unknown): value is undefined | null | '';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * - Get the value from an object using a path
3
+ * - You can retrieve a nested value by using a dot notation path
4
+ */
5
+ export declare function getValue(data: unknown, key: unknown): unknown;
6
+ /**
7
+ * Is the value undefined or null?
8
+ */
9
+ export declare function isNullable(value: unknown): value is undefined | null;