@natachah/vanilla-frontend 0.0.2
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/.gitlab-ci.yml +40 -0
- package/LICENSE.md +7 -0
- package/README.md +11 -0
- package/docs/index.html +36 -0
- package/docs/main.js +32 -0
- package/docs/pages/components/badge.html +154 -0
- package/docs/pages/components/button.html +186 -0
- package/docs/pages/components/card.html +184 -0
- package/docs/pages/components/dialog.html +334 -0
- package/docs/pages/components/disclosure.html +310 -0
- package/docs/pages/components/dropdown.html +255 -0
- package/docs/pages/components/form.html +331 -0
- package/docs/pages/components/list.html +140 -0
- package/docs/pages/components/loading.html +58 -0
- package/docs/pages/components/media.html +130 -0
- package/docs/pages/components/nav.html +119 -0
- package/docs/pages/components/progress.html +47 -0
- package/docs/pages/components/slider.html +311 -0
- package/docs/pages/components/table.html +168 -0
- package/docs/pages/javascript/autofill.html +170 -0
- package/docs/pages/javascript/checkall.html +59 -0
- package/docs/pages/javascript/comfort.html +134 -0
- package/docs/pages/javascript/consent.html +112 -0
- package/docs/pages/javascript/cookie.html +81 -0
- package/docs/pages/javascript/form.html +199 -0
- package/docs/pages/javascript/scroll.html +209 -0
- package/docs/pages/javascript/sidebar.html +53 -0
- package/docs/pages/javascript/sortable.html +148 -0
- package/docs/pages/javascript/toggle.html +191 -0
- package/docs/pages/javascript/tree.html +221 -0
- package/docs/pages/layout/grid.html +201 -0
- package/docs/pages/layout/reset.html +53 -0
- package/docs/pages/layout/typography.html +324 -0
- package/docs/pages/quick-start/conventions.html +112 -0
- package/docs/pages/quick-start/customization.html +187 -0
- package/docs/pages/quick-start/installation.html +95 -0
- package/docs/pages/quick-start/mixins.html +228 -0
- package/docs/pages/test.html +15 -0
- package/docs/src/js/demo.js +98 -0
- package/docs/src/js/doc-code.js +102 -0
- package/docs/src/js/doc-demo.js +14 -0
- package/docs/src/js/doc-layout.js +108 -0
- package/docs/src/scss/demo.scss +77 -0
- package/docs/src/scss/layout.scss +160 -0
- package/docs/src/scss/style.scss +278 -0
- package/docs/vite.config.mjs +23 -0
- package/esbuild.mjs +25 -0
- package/js/_autofill.js +131 -0
- package/js/_check-all.js +77 -0
- package/js/_comfort.js +174 -0
- package/js/_consent.js +84 -0
- package/js/_dialog.js +164 -0
- package/js/_dropdown.js +101 -0
- package/js/_scroll.js +184 -0
- package/js/_sidebar.js +97 -0
- package/js/_slider.js +249 -0
- package/js/_sortable.js +143 -0
- package/js/_tabpanel.js +88 -0
- package/js/_toggle.js +123 -0
- package/js/_tree.js +85 -0
- package/js/tests/autofill.test.js +157 -0
- package/js/tests/base-component.test.js +108 -0
- package/js/tests/check-all.test.js +88 -0
- package/js/tests/comfort.test.js +219 -0
- package/js/tests/consent.test.js +84 -0
- package/js/tests/cookie.test.js +102 -0
- package/js/tests/dialog.test.js +189 -0
- package/js/tests/dropdown.test.js +115 -0
- package/js/tests/form-helper.test.js +155 -0
- package/js/tests/scroll.test.js +203 -0
- package/js/tests/sidebar.test.js +99 -0
- package/js/tests/slider.test.js +307 -0
- package/js/tests/sortable.test.js +124 -0
- package/js/tests/tabpanel.test.js +114 -0
- package/js/tests/toggle.test.js +190 -0
- package/js/tests/tree.test.js +165 -0
- package/js/utilities/_base-component.js +101 -0
- package/js/utilities/_cookie.js +98 -0
- package/js/utilities/_error.js +80 -0
- package/js/utilities/_form-helper.js +101 -0
- package/package.json +42 -0
- package/scss/_badge.scss +37 -0
- package/scss/_button.scss +34 -0
- package/scss/_card.scss +122 -0
- package/scss/_dialog.scss +116 -0
- package/scss/_disclosure.scss +101 -0
- package/scss/_dropdown.scss +68 -0
- package/scss/_form.scss +197 -0
- package/scss/_grid.scss +40 -0
- package/scss/_group.scss +57 -0
- package/scss/_list.scss +18 -0
- package/scss/_loading.scss +49 -0
- package/scss/_media.scss +37 -0
- package/scss/_nav.scss +72 -0
- package/scss/_progress.scss +40 -0
- package/scss/_slider.scss +35 -0
- package/scss/_table.scss +36 -0
- package/scss/utilities/_mixin.scss +322 -0
- package/scss/utilities/_reset.scss +145 -0
- package/scss/utilities/_typography.scss +107 -0
- package/scss/vanilla-frontend.scss +23 -0
- package/scss/variables/_root.scss +70 -0
- package/scss/variables/_setting.scss +63 -0
- package/vitest.config.js +7 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ------------------------------------------------------------------
|
|
3
|
+
* Form helper
|
|
4
|
+
* ------------------------------------------------------------------
|
|
5
|
+
* This class regroup some helper function for forms
|
|
6
|
+
*
|
|
7
|
+
* @author Natacha Herth
|
|
8
|
+
* @version 0.0.1
|
|
9
|
+
* @copyright Natacha Herth, design & web development
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import ErrorMessage from "./_error"
|
|
13
|
+
|
|
14
|
+
export default class FormHelper {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Toggle the visibility of a password input
|
|
18
|
+
*
|
|
19
|
+
* @param {HTMLElement} button - A <button> element
|
|
20
|
+
*/
|
|
21
|
+
static togglePassword(button) {
|
|
22
|
+
|
|
23
|
+
// Check for errors
|
|
24
|
+
if (!(button instanceof HTMLElement)) throw new Error(ErrorMessage.instanceOf('button', 'HTMLElement'))
|
|
25
|
+
if (!button.hasAttribute('aria-controls')) throw new Error(ErrorMessage.withAttribute('button', 'aria-controls'))
|
|
26
|
+
|
|
27
|
+
// Define the <input>
|
|
28
|
+
const input = document.getElementById(button.getAttribute('aria-controls'))
|
|
29
|
+
|
|
30
|
+
// If no <input> return error
|
|
31
|
+
if (!input) throw new Error(ErrorMessage.existById('input', button.getAttribute('aria-controls')))
|
|
32
|
+
|
|
33
|
+
// Define the new [type] of the <input>
|
|
34
|
+
const type = input.type === 'password' ? 'text' : 'password'
|
|
35
|
+
input.setAttribute('type', type)
|
|
36
|
+
|
|
37
|
+
// Change the [aria-pressed] attribute on the <button>
|
|
38
|
+
button.setAttribute('aria-pressed', type !== 'password')
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Toggle the attributes value, disabled and required on multiple fields when is visible
|
|
44
|
+
*
|
|
45
|
+
* @param {Object} fields - Array of fields to toggle
|
|
46
|
+
* @param {boolean} isVisible - Are the fields visible
|
|
47
|
+
* @param {Object} customOptions - Object of options
|
|
48
|
+
*/
|
|
49
|
+
static toggleAttributes(fields, isVisible, customOptions = {}) {
|
|
50
|
+
|
|
51
|
+
// Check for errors
|
|
52
|
+
if (!(fields instanceof Object)) throw new Error(ErrorMessage.instanceOf('fields', 'Object'))
|
|
53
|
+
if (typeof isVisible !== 'boolean') throw new Error(ErrorMessage.typeOf('isVisible', 'boolean'))
|
|
54
|
+
if (!(customOptions instanceof Object)) throw new Error(ErrorMessage.instanceOf('customOptions', 'Object'))
|
|
55
|
+
if (customOptions.reset && !['boolean', 'object'].includes(typeof customOptions.reset)) throw new Error(ErrorMessage.typeOf('customOptions.reset', 'boolean|object'))
|
|
56
|
+
if (customOptions.disabled && !['boolean', 'object'].includes(typeof customOptions.disabled)) throw new Error(ErrorMessage.typeOf('customOptions.disabled', 'boolean|object'))
|
|
57
|
+
if (customOptions.required && !['boolean', 'object'].includes(typeof customOptions.required)) throw new Error(ErrorMessage.typeOf('customOptions.required', 'boolean|object'))
|
|
58
|
+
if (customOptions.unchanged && typeof customOptions.unchanged !== 'object') throw new Error(ErrorMessage.typeOf('customOptions.unchanged', 'object'))
|
|
59
|
+
|
|
60
|
+
// Set default options
|
|
61
|
+
const defaultOption = {
|
|
62
|
+
reset: true, // Can be true/false/[name]
|
|
63
|
+
disabled: true, // Can be true/false/[name]
|
|
64
|
+
required: false, // Can be true/false/[name]
|
|
65
|
+
unchanged: [] // Array of names
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Merge the options
|
|
69
|
+
const options = { ...defaultOption, ...customOptions }
|
|
70
|
+
|
|
71
|
+
// Do stuff for each field
|
|
72
|
+
fields.forEach(field => {
|
|
73
|
+
|
|
74
|
+
// Get the name
|
|
75
|
+
const name = field.getAttribute('name')
|
|
76
|
+
|
|
77
|
+
// Stop if the field must not change
|
|
78
|
+
if (options.unchanged.includes(name)) return
|
|
79
|
+
|
|
80
|
+
// Get the needs
|
|
81
|
+
let needReset = typeof options.reset === 'boolean' ? options.reset : options.reset.includes(name)
|
|
82
|
+
let needDisabled = typeof options.disabled === 'boolean' ? options.disabled : options.disabled.includes(name)
|
|
83
|
+
let needRequired = typeof options.required === 'boolean' ? options.required : options.required.includes(name)
|
|
84
|
+
|
|
85
|
+
// Reset the field
|
|
86
|
+
if (needReset && !isVisible) {
|
|
87
|
+
field.value = null
|
|
88
|
+
field.checked = null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Disabled field
|
|
92
|
+
if (needDisabled) field.disabled = !isVisible
|
|
93
|
+
|
|
94
|
+
// Required field
|
|
95
|
+
if (needRequired) field.required = isVisible
|
|
96
|
+
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@natachah/vanilla-frontend",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "A vanilla frontend framework",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"html5",
|
|
7
|
+
"css3",
|
|
8
|
+
"javascript",
|
|
9
|
+
"vanilla",
|
|
10
|
+
"frontend"
|
|
11
|
+
],
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "Natacha Herth",
|
|
14
|
+
"email": "info@natachaherth.ch"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "vitest --coverage",
|
|
19
|
+
"code:build": "node esbuild.mjs",
|
|
20
|
+
"docs:dev": "cd docs && vite",
|
|
21
|
+
"docs:build": "cd docs && vite build",
|
|
22
|
+
"docs:preview": "cd docs && vite preview"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+ssh://git@gitlab.com:packages4913705/vanilla-frontend.git"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@testing-library/dom": "^9.3.4",
|
|
30
|
+
"@vitest/coverage-v8": "^1.3.1",
|
|
31
|
+
"autoprefixer": "^10.4.19",
|
|
32
|
+
"esbuild": "0.21.5",
|
|
33
|
+
"esbuild-sass-plugin": "^3.3.1",
|
|
34
|
+
"fast-glob": "^3.3.2",
|
|
35
|
+
"happy-dom": "^13.6.2",
|
|
36
|
+
"postcss": "^8.4.38",
|
|
37
|
+
"sass": "^1.77.5",
|
|
38
|
+
"shiki": "^1.7.0",
|
|
39
|
+
"vite": "^5.2.0",
|
|
40
|
+
"vitest": "^1.3.1"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/scss/_badge.scss
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
////
|
|
2
|
+
/// ------------------------------------------------------------------
|
|
3
|
+
/// Badge
|
|
4
|
+
/// ------------------------------------------------------------------
|
|
5
|
+
/// Create the badge component
|
|
6
|
+
///
|
|
7
|
+
/// @example <span class="badge">...</span>
|
|
8
|
+
///
|
|
9
|
+
/// @require {mixin} as-item
|
|
10
|
+
/// @group components
|
|
11
|
+
/// @author Natacha Herth
|
|
12
|
+
/// @since 1.0.0
|
|
13
|
+
///
|
|
14
|
+
////
|
|
15
|
+
|
|
16
|
+
@use "sass:map";
|
|
17
|
+
|
|
18
|
+
$custom: (
|
|
19
|
+
background: color-mix(in srgb, transparent, var(--hover-color) var(--hover-percent)),
|
|
20
|
+
padding-block: .25em,
|
|
21
|
+
padding-inline: .375em
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// Change default properties
|
|
25
|
+
$properties: map.merge($default-item-properties, $custom);
|
|
26
|
+
|
|
27
|
+
.badge {
|
|
28
|
+
|
|
29
|
+
// Customization for badge
|
|
30
|
+
display: inline-block;
|
|
31
|
+
font-size: var(--badge-font-size, var(--font-size-small));
|
|
32
|
+
line-height: 1;
|
|
33
|
+
|
|
34
|
+
// Define as an item
|
|
35
|
+
@include as-item('badge', (), $properties);
|
|
36
|
+
|
|
37
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
////
|
|
2
|
+
/// ------------------------------------------------------------------
|
|
3
|
+
/// Button
|
|
4
|
+
/// ------------------------------------------------------------------
|
|
5
|
+
/// Create the button component
|
|
6
|
+
///
|
|
7
|
+
/// @example <button>Button</button> or <a href="#" role="button">Button</a>
|
|
8
|
+
///
|
|
9
|
+
/// @require {mixin} as-item
|
|
10
|
+
/// @group components
|
|
11
|
+
/// @author Natacha Herth
|
|
12
|
+
/// @since 1.0.0
|
|
13
|
+
///
|
|
14
|
+
////
|
|
15
|
+
|
|
16
|
+
button,
|
|
17
|
+
[type=button],
|
|
18
|
+
[type=reset],
|
|
19
|
+
[type=submit],
|
|
20
|
+
[role=button] {
|
|
21
|
+
|
|
22
|
+
// Customization for button
|
|
23
|
+
display: inline-block;
|
|
24
|
+
|
|
25
|
+
// Define as an item
|
|
26
|
+
@include as-item('button', ('focus', 'hover', 'active', 'disabled'));
|
|
27
|
+
|
|
28
|
+
// Set as :disabled for link without href
|
|
29
|
+
&:is(a):not([href]) {
|
|
30
|
+
pointer-events: none;
|
|
31
|
+
opacity: var(--button-disabled-opacity, var(--disabled-opacity));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
}
|
package/scss/_card.scss
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
////
|
|
2
|
+
/// ------------------------------------------------------------------
|
|
3
|
+
/// Card
|
|
4
|
+
/// ------------------------------------------------------------------
|
|
5
|
+
/// Create the card component
|
|
6
|
+
///
|
|
7
|
+
/// @example <div class="card">...</div>
|
|
8
|
+
///
|
|
9
|
+
/// @require {mixin} as-item
|
|
10
|
+
/// @group components
|
|
11
|
+
/// @author Natacha Herth
|
|
12
|
+
/// @since 1.0.0
|
|
13
|
+
///
|
|
14
|
+
////
|
|
15
|
+
|
|
16
|
+
@use "sass:map";
|
|
17
|
+
|
|
18
|
+
$custom: (
|
|
19
|
+
padding-block: 1rem,
|
|
20
|
+
padding-inline: 1rem
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Change default properties
|
|
24
|
+
$properties: map.merge($default-item-properties, $custom);
|
|
25
|
+
|
|
26
|
+
.card {
|
|
27
|
+
|
|
28
|
+
// Design as item
|
|
29
|
+
@include as-item('card', (), $properties);
|
|
30
|
+
|
|
31
|
+
// Design the layout
|
|
32
|
+
> header,
|
|
33
|
+
> footer,
|
|
34
|
+
> picture,
|
|
35
|
+
> .list,
|
|
36
|
+
> .accordion,
|
|
37
|
+
> .group {
|
|
38
|
+
margin-inline: calc(var(--card-padding-inline, map.get($custom, padding-inline)) * -1);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
> header,
|
|
42
|
+
> footer {
|
|
43
|
+
padding: calc(var(--card-padding-block, map.get($custom, padding-block)) / 2) var(--card-padding-inline, map.get($custom, padding-inline));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
> header,
|
|
47
|
+
> :first-child:is(picture, .list, .group) {
|
|
48
|
+
margin-top: calc(var(--card-padding-block, map.get($custom, padding-block)) * -1);
|
|
49
|
+
border-start-start-radius: inherit;
|
|
50
|
+
border-start-end-radius: inherit;
|
|
51
|
+
border-bottom: var(--card-divider-size, var(--card-border-size, var(--border-size))) var(--card-divider-style, var(--card-border-style, var(--border-style))) var(--card-divider-color, var(--card-border-color, transparent));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
> footer,
|
|
55
|
+
> :last-child:is(picture, .list, .group) {
|
|
56
|
+
margin-bottom: calc(var(--card-padding-block, map.get($custom, padding-block)) * -1);
|
|
57
|
+
border-end-start-radius: inherit;
|
|
58
|
+
border-end-end-radius: inherit;
|
|
59
|
+
border-top: var(--card-divider-size, var(--card-border-size, var(--border-size))) var(--card-divider-style, var(--card-border-style, var(--border-style))) var(--card-divider-color, var(--card-border-color, transparent));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
> :not(:first-child, :last-child):is(picture, .list, .group) {
|
|
63
|
+
border-top: var(--card-divider-size, var(--card-border-size, var(--border-size))) var(--card-divider-style, var(--card-border-style, var(--border-style))) var(--card-divider-color, var(--card-border-color, transparent));
|
|
64
|
+
border-bottom: var(--card-divider-size, var(--card-border-size, var(--border-size))) var(--card-divider-style, var(--card-border-style, var(--border-style))) var(--card-divider-color, var(--card-border-color, transparent));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
> picture {
|
|
68
|
+
max-width: inherit;
|
|
69
|
+
overflow: hidden;
|
|
70
|
+
|
|
71
|
+
> img {
|
|
72
|
+
width: 100%;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
> .list {
|
|
77
|
+
|
|
78
|
+
> *,
|
|
79
|
+
> * > a {
|
|
80
|
+
border-inline: none !important;
|
|
81
|
+
border-radius: inherit !important;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
> *:first-child,
|
|
85
|
+
> *:first-child > a {
|
|
86
|
+
border-top: none !important;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
> *:last-child,
|
|
90
|
+
> *:last-child > a {
|
|
91
|
+
border-bottom: none !important;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
> .group {
|
|
97
|
+
|
|
98
|
+
> * {
|
|
99
|
+
width: 100%;
|
|
100
|
+
border-radius: inherit;
|
|
101
|
+
|
|
102
|
+
&:first-child {
|
|
103
|
+
border-left: none;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
&:last-child {
|
|
107
|
+
border-right: none;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
&:is(:first-child) > * {
|
|
113
|
+
border-top: none;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
&:is(:last-child) > * {
|
|
117
|
+
border-bottom: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
////
|
|
2
|
+
/// ------------------------------------------------------------------
|
|
3
|
+
/// Dialog
|
|
4
|
+
/// ------------------------------------------------------------------
|
|
5
|
+
/// Create the dialog component
|
|
6
|
+
///
|
|
7
|
+
/// @example <dialog>My dialog</dialog>
|
|
8
|
+
///
|
|
9
|
+
/// @group components
|
|
10
|
+
/// @author Natacha Herth
|
|
11
|
+
/// @since 1.0.0
|
|
12
|
+
///
|
|
13
|
+
////
|
|
14
|
+
|
|
15
|
+
@use "sass:map";
|
|
16
|
+
|
|
17
|
+
$custom: (
|
|
18
|
+
background: var(--color-body),
|
|
19
|
+
padding-block: 2rem,
|
|
20
|
+
padding-inline: 2rem
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Change default properties
|
|
24
|
+
$properties: map.merge($default-item-properties, $custom);
|
|
25
|
+
|
|
26
|
+
dialog {
|
|
27
|
+
|
|
28
|
+
// Position the dialog
|
|
29
|
+
position: fixed;
|
|
30
|
+
inset: var(--dialog-position, 0);
|
|
31
|
+
|
|
32
|
+
// Define the CSS
|
|
33
|
+
margin: auto;
|
|
34
|
+
width: var(--dialog-width, 576px);
|
|
35
|
+
height: var(--dialog-height, fit-content);
|
|
36
|
+
max-width: var(--dialog-max-width, 100dvw);
|
|
37
|
+
max-height: var(--dialog-max-height, 100dvh);
|
|
38
|
+
overflow: auto;
|
|
39
|
+
|
|
40
|
+
// Design as item
|
|
41
|
+
@include as-item('dialog', (), $properties);
|
|
42
|
+
|
|
43
|
+
// Animate the dialog
|
|
44
|
+
@media screen and (prefers-reduced-motion: no-preference) {
|
|
45
|
+
animation: var(--dialog-close-animation, close-dialog 0.25s ease-in-out);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Display dialog when open
|
|
49
|
+
// Because of the [inert] attribute on the body, require the pointer-events
|
|
50
|
+
&[open] {
|
|
51
|
+
z-index: var(--dialog-index, 1);
|
|
52
|
+
pointer-events: auto;
|
|
53
|
+
|
|
54
|
+
@media screen and (prefers-reduced-motion: no-preference) {
|
|
55
|
+
animation: var(--dialog-open-animation, open-dialog 0.25s ease-in-out);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Design the backdrop, can't be updated per dialog
|
|
60
|
+
&::backdrop {
|
|
61
|
+
background: var(--dialog-backdrop-background, var(--backdrop-color, rgba(black, .75)));
|
|
62
|
+
backdrop-filter: var(--dialog-backdrop-filter, var(--backdrop-filter, blur(.5rem)));
|
|
63
|
+
-webkit-backdrop-filter: var(--dialog-backdrop-filter, var(--backdrop-filter, blur(.5rem)));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Design the layout
|
|
67
|
+
> header,
|
|
68
|
+
> footer {
|
|
69
|
+
margin-inline: calc(var(--dialog-padding-inline, map.get($custom, padding-inline)) * -1);
|
|
70
|
+
padding: calc(var(--dialog-padding-block, map.get($custom, padding-block)) / 2) var(--dialog-padding-inline, map.get($custom, padding-inline));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
> header {
|
|
74
|
+
margin-top: calc(var(--dialog-padding-block, map.get($custom, padding-block)) * -1);
|
|
75
|
+
border-start-start-radius: inherit;
|
|
76
|
+
border-start-end-radius: inherit;
|
|
77
|
+
border-bottom: var(--dialog-divider-size, var(--dialog-border-size, var(--border-size))) var(--dialog-divider-style, var(--dialog-border-style, var(--border-style))) var(--dialog-divider-color, var(--dialog-border-color, transparent));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
> footer {
|
|
81
|
+
margin-bottom: calc(var(--dialog-padding-block, map.get($custom, padding-block)) * -1);
|
|
82
|
+
border-end-start-radius: inherit;
|
|
83
|
+
border-end-end-radius: inherit;
|
|
84
|
+
border-top: var(--dialog-divider-size, var(--dialog-border-size, var(--border-size))) var(--dialog-divider-style, var(--dialog-border-style, var(--border-style))) var(--dialog-divider-color, var(--dialog-border-color, transparent));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@keyframes open-dialog {
|
|
90
|
+
from {
|
|
91
|
+
opacity: 0;
|
|
92
|
+
transform: var(--dialog-open-transform, translateY(100px));
|
|
93
|
+
display: none;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
to {
|
|
97
|
+
opacity: 1;
|
|
98
|
+
transform: none;
|
|
99
|
+
display: block;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// NOT work on FF
|
|
104
|
+
@keyframes close-dialog {
|
|
105
|
+
from {
|
|
106
|
+
opacity: 1;
|
|
107
|
+
transform: none;
|
|
108
|
+
display: block;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
to {
|
|
112
|
+
opacity: 0;
|
|
113
|
+
transform: var(--dialog-close-transform, translateY(100px));
|
|
114
|
+
display: none;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
////
|
|
2
|
+
/// ------------------------------------------------------------------
|
|
3
|
+
/// Disclosure
|
|
4
|
+
/// ------------------------------------------------------------------
|
|
5
|
+
/// Create the disclosure component
|
|
6
|
+
///
|
|
7
|
+
/// @example <details><summary>Click</summary><div>My disclosure</div></details>
|
|
8
|
+
///
|
|
9
|
+
/// @require {mixin} as-item
|
|
10
|
+
/// @group components
|
|
11
|
+
/// @author Natacha Herth
|
|
12
|
+
/// @since 1.0.0
|
|
13
|
+
///
|
|
14
|
+
////
|
|
15
|
+
|
|
16
|
+
details {
|
|
17
|
+
|
|
18
|
+
> summary {
|
|
19
|
+
|
|
20
|
+
// Define as an item
|
|
21
|
+
@include as-item('disclosure', ('focus', 'hover'));
|
|
22
|
+
|
|
23
|
+
// Animate the SVG
|
|
24
|
+
@media screen and (prefers-reduced-motion: no-preference) {
|
|
25
|
+
> svg {
|
|
26
|
+
transition: var(--disclosure-transition, all .25s ease-in-out);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Define as an item
|
|
33
|
+
> div {
|
|
34
|
+
@include as-item('disclosure');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
&[open] summary {
|
|
38
|
+
|
|
39
|
+
// Define manually the :active state
|
|
40
|
+
color: var(--disclosure-active-color, var(--disclosure-color, var(--color-font)));
|
|
41
|
+
background-color: var(--disclosure-active-background, color-mix(in srgb, var(--disclosure-background, transparent), var(--active-color) var(--active-percent)));
|
|
42
|
+
border-color: var(--disclosure-active-border-color, var(--disclosure-border-color, transparent));
|
|
43
|
+
|
|
44
|
+
// Transform the SVG
|
|
45
|
+
> svg {
|
|
46
|
+
transform: var(--disclosure-svg-transform, rotate(90deg));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Include the color variation on the parent
|
|
52
|
+
@include with-color-variations('disclosure');
|
|
53
|
+
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.accordion {
|
|
57
|
+
|
|
58
|
+
// Remove extra border
|
|
59
|
+
> details + details > summary,
|
|
60
|
+
> details > div {
|
|
61
|
+
border-top: none !important;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Remove extra radius
|
|
65
|
+
> details:not(:first-child, :last-child) > summary,
|
|
66
|
+
> details:not(:last-child) > div {
|
|
67
|
+
border-radius: 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Remove radius on last element
|
|
71
|
+
> details:last-child[open] {
|
|
72
|
+
|
|
73
|
+
> summary {
|
|
74
|
+
border-end-start-radius: 0;
|
|
75
|
+
border-end-end-radius: 0;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
> div {
|
|
79
|
+
border-start-start-radius: 0;
|
|
80
|
+
border-start-end-radius: 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
> details:not(:only-child) {
|
|
86
|
+
|
|
87
|
+
// Remove radius bottom on first child
|
|
88
|
+
&:first-child > summary {
|
|
89
|
+
border-end-start-radius: 0;
|
|
90
|
+
border-end-end-radius: 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Remove radius top on last child
|
|
94
|
+
&:last-child > summary {
|
|
95
|
+
border-start-start-radius: 0;
|
|
96
|
+
border-start-end-radius: 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
////
|
|
2
|
+
/// ------------------------------------------------------------------
|
|
3
|
+
/// Dropdown
|
|
4
|
+
/// ------------------------------------------------------------------
|
|
5
|
+
/// Create the dropdown component
|
|
6
|
+
///
|
|
7
|
+
/// @example <div class="dropdown"><button aria-controls="myDropdown" aria-expanded="false" aria-pressed="false">Button</button><ul id="myDropdown" hidden>...</ul></div>
|
|
8
|
+
///
|
|
9
|
+
/// @require {mixin} as-list
|
|
10
|
+
/// @group components
|
|
11
|
+
/// @author Natacha Herth
|
|
12
|
+
/// @since 1.0.0
|
|
13
|
+
///
|
|
14
|
+
////
|
|
15
|
+
|
|
16
|
+
@use "sass:map";
|
|
17
|
+
|
|
18
|
+
$custom: (
|
|
19
|
+
background: var(--color-body)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// Change default properties
|
|
23
|
+
$properties: map.merge($default-item-properties, $custom);
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
.dropdown {
|
|
27
|
+
|
|
28
|
+
position: relative;
|
|
29
|
+
display: inline-block;
|
|
30
|
+
|
|
31
|
+
> [aria-controls] {
|
|
32
|
+
|
|
33
|
+
width: 100%;
|
|
34
|
+
white-space: nowrap;
|
|
35
|
+
|
|
36
|
+
// Animate the SVG
|
|
37
|
+
@media screen and (prefers-reduced-motion: no-preference) {
|
|
38
|
+
> svg {
|
|
39
|
+
transition: var(--dropdown-transition, all .25s ease-in-out);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
&[aria-expanded=true] > svg {
|
|
44
|
+
transform: var(--dropdown-svg-transform, rotate(-180deg));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
> *:not([aria-controls]) {
|
|
50
|
+
|
|
51
|
+
// Make it overflow
|
|
52
|
+
position: absolute;
|
|
53
|
+
z-index: var(--dropdown-index, 1);
|
|
54
|
+
top: calc(100% + var(--dropdown-offset, .5rem));
|
|
55
|
+
|
|
56
|
+
// Simple bloc
|
|
57
|
+
&:not(ul, li) {
|
|
58
|
+
@include as-item('dropdown', (), $properties);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// As list
|
|
62
|
+
&:is(ul, ol) {
|
|
63
|
+
@include as-list('dropdown', ('focus', 'hover', 'active', 'disabled'), $properties);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
}
|