@uncinq/css-components 0.1.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 +21 -0
- package/README.md +125 -0
- package/css/component/alert.css +40 -0
- package/css/component/badge.css +23 -0
- package/css/component/banner.css +29 -0
- package/css/component/breadcrumb.css +43 -0
- package/css/component/button.css +103 -0
- package/css/component/card.css +54 -0
- package/css/component/dropdown.css +125 -0
- package/css/component/embed.css +26 -0
- package/css/component/list.css +36 -0
- package/css/component/map.css +25 -0
- package/css/component/media.css +40 -0
- package/css/component/nav-accessibility.css +39 -0
- package/css/component/nav.css +80 -0
- package/css/component/offcanvas.css +177 -0
- package/css/component/pagination.css +75 -0
- package/css/index.css +16 -0
- package/package.json +37 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Un Cinq
|
|
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,125 @@
|
|
|
1
|
+
# @uncinq/css-components
|
|
2
|
+
|
|
3
|
+
> Framework-agnostic CSS component implementations — actual styles built on top of design and component tokens.
|
|
4
|
+
|
|
5
|
+
<img width="1280" height="640" alt="share-css-components" src="https://github.com/user-attachments/assets/3d74df8a-e7bf-4ba5-8529-99cfef0bc176" />
|
|
6
|
+
|
|
7
|
+
## What is this?
|
|
8
|
+
|
|
9
|
+
`@uncinq/css-components` provides the CSS implementations of common UI components. It sits at the top of the CSS stack:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
@uncinq/design-tokens ← primitive + semantic CSS custom properties
|
|
13
|
+
@uncinq/component-tokens ← component-scoped CSS custom properties
|
|
14
|
+
@uncinq/css-base ← base CSS rules
|
|
15
|
+
@uncinq/css-components ← actual CSS rules (this package)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Each component file contains only CSS rules, using the tokens defined in the two packages above. No JavaScript, no markup opinions — just portable, layered CSS.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## CSS cascade layers
|
|
23
|
+
|
|
24
|
+
All component styles are declared inside `@layer components`:
|
|
25
|
+
|
|
26
|
+
```css
|
|
27
|
+
@layer config, base, layouts, vendors, components;
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This means:
|
|
31
|
+
|
|
32
|
+
- Components never bleed into base or layout styles
|
|
33
|
+
- Project-level `@layer components` rules always win over these defaults
|
|
34
|
+
- Vendors (e.g. Splide) sit below project components but above base
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Prerequisites
|
|
39
|
+
|
|
40
|
+
This package consumes tokens from:
|
|
41
|
+
|
|
42
|
+
- [@uncinq/design-tokens](https://github.com/uncinq/design-tokens) — semantic CSS custom properties
|
|
43
|
+
- [@uncinq/component-tokens](https://github.com/uncinq/component-tokens) — component-scoped CSS custom properties
|
|
44
|
+
- [@uncinq/css-base](https://github.com/uncinq/css-base) — base CSS rules
|
|
45
|
+
|
|
46
|
+
Import them before this package:
|
|
47
|
+
|
|
48
|
+
```css
|
|
49
|
+
@import '@uncinq/design-tokens';
|
|
50
|
+
@import '@uncinq/component-tokens';
|
|
51
|
+
@import '@uncinq/css-base';
|
|
52
|
+
@import '@uncinq/css-components';
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Installation
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install @uncinq/css-components
|
|
61
|
+
# or
|
|
62
|
+
yarn add @uncinq/css-components
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Usage — full import
|
|
66
|
+
|
|
67
|
+
```css
|
|
68
|
+
@import '@uncinq/design-tokens';
|
|
69
|
+
@import '@uncinq/component-tokens';
|
|
70
|
+
@import '@uncinq/css-base';
|
|
71
|
+
@import '@uncinq/css-components';
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Usage — per component
|
|
75
|
+
|
|
76
|
+
```css
|
|
77
|
+
@import '@uncinq/design-tokens';
|
|
78
|
+
@import '@uncinq/component-tokens';
|
|
79
|
+
@import '@uncinq/css-base';
|
|
80
|
+
@import '@uncinq/css-components/css/component/nav.css';
|
|
81
|
+
@import '@uncinq/css-components/css/component/pagination.css';
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Usage — CDN (no build step)
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<link rel="stylesheet" href="https://unpkg.com/@uncinq/design-tokens/tokens/index.css">
|
|
88
|
+
<link rel="stylesheet" href="https://unpkg.com/@uncinq/component-tokens/tokens/index.css">
|
|
89
|
+
<link rel="stylesheet" href="https://unpkg.com/@uncinq/css-base/css/index.css">
|
|
90
|
+
<link rel="stylesheet" href="https://unpkg.com/@uncinq/css-components/css/index.css">
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## File structure
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
css/
|
|
99
|
+
index.css ← imports all component files
|
|
100
|
+
component/
|
|
101
|
+
alert.css ← alert / notification banner
|
|
102
|
+
badge.css ← badge / pill / tag
|
|
103
|
+
banner.css ← full-width banner strip
|
|
104
|
+
breadcrumb.css ← breadcrumb navigation
|
|
105
|
+
button.css ← button (all variants)
|
|
106
|
+
card.css ← card container
|
|
107
|
+
dropdown.css ← dropdown menu
|
|
108
|
+
embed.css ← video / iframe embed wrapper
|
|
109
|
+
list.css ← styled list
|
|
110
|
+
map.css ← embedded map
|
|
111
|
+
media.css ← media object (image + text)
|
|
112
|
+
nav.css ← navigation bar
|
|
113
|
+
nav-accessibility.css ← accessibility skip links / focus helpers
|
|
114
|
+
offcanvas.css ← off-canvas panel / drawer
|
|
115
|
+
pagination.css ← pagination control
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## References
|
|
121
|
+
|
|
122
|
+
- [@uncinq/design-tokens](https://github.com/uncinq/design-tokens) — primitive + semantic layers
|
|
123
|
+
- [@uncinq/component-tokens](https://github.com/uncinq/component-tokens) — component token layer
|
|
124
|
+
- [@uncinq/css-base](https://github.com/uncinq/css-base) — base CSS rules
|
|
125
|
+
- [MDN: CSS cascade layers](https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Styling_basics/Cascade_layers)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* components/alert.css */
|
|
2
|
+
@layer components {
|
|
3
|
+
.alert {
|
|
4
|
+
background-color: var(--alert-color-bg);
|
|
5
|
+
border-radius: var(--alert-border-radius);
|
|
6
|
+
color: var(--alert-color-text);
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
font-size: var(--alert-font-size);
|
|
10
|
+
gap: var(--alert-gap);
|
|
11
|
+
margin: var(--alert-margin);
|
|
12
|
+
padding: var(--alert-padding);
|
|
13
|
+
|
|
14
|
+
> * {
|
|
15
|
+
margin: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.alert-heading {
|
|
19
|
+
align-items: center;
|
|
20
|
+
display: flex;
|
|
21
|
+
font-weight: var(--font-weight-semibold);
|
|
22
|
+
gap: var(--alert-gap);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&:has(.icon:first-child):not(:has(.alert-heading)) {
|
|
26
|
+
flex-direction: row;
|
|
27
|
+
align-items: flex-start;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Variants */
|
|
32
|
+
.alert-danger { --alert-color-bg: var(--color-danger-muted); --alert-color-text: var(--color-danger-strong); }
|
|
33
|
+
.alert-dark { --alert-color-bg: var(--color-dark-muted); --alert-color-text: var(--color-dark-strong); }
|
|
34
|
+
.alert-info { --alert-color-bg: var(--color-info-muted); --alert-color-text: var(--color-info-strong); }
|
|
35
|
+
.alert-light { --alert-color-bg: var(--color-light-muted); --alert-color-text: var(--color-light-strong); }
|
|
36
|
+
.alert-primary { --alert-color-bg: var(--color-primary-muted); --alert-color-text: var(--color-primary-strong); }
|
|
37
|
+
.alert-secondary { --alert-color-bg: var(--color-secondary-muted); --alert-color-text: var(--color-secondary-strong); }
|
|
38
|
+
.alert-success { --alert-color-bg: var(--color-success-muted); --alert-color-text: var(--color-success-strong); }
|
|
39
|
+
.alert-warning { --alert-color-bg: var(--color-warning-muted); --alert-color-text: var(--color-warning-strong); }
|
|
40
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* components/badge.css */
|
|
2
|
+
@layer components {
|
|
3
|
+
.badge {
|
|
4
|
+
background-color: var(--badge-color-bg);
|
|
5
|
+
border: var(--badge-border-width) var(--badge-border-style) var(--badge-color-border);
|
|
6
|
+
border-radius: var(--badge-border-radius);
|
|
7
|
+
color: var(--badge-color-text);
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
font-size: var(--badge-font-size);
|
|
10
|
+
font-weight: var(--badge-font-weight);
|
|
11
|
+
padding: var(--badge-padding-y) var(--badge-padding-x);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* Variants */
|
|
15
|
+
.badge-danger { --badge-color-bg: var(--color-danger-muted); --badge-color-text: var(--color-danger-strong); }
|
|
16
|
+
.badge-dark { --badge-color-bg: var(--color-dark-muted); --badge-color-text: var(--color-dark-strong); }
|
|
17
|
+
.badge-info { --badge-color-bg: var(--color-info-muted); --badge-color-text: var(--color-info-strong); }
|
|
18
|
+
.badge-light { --badge-color-bg: var(--color-light-muted); --badge-color-text: var(--color-light-strong); }
|
|
19
|
+
.badge-primary { --badge-color-bg: var(--color-primary-muted); --badge-color-text: var(--color-primary-strong); }
|
|
20
|
+
.badge-secondary { --badge-color-bg: var(--color-secondary-muted); --badge-color-text: var(--color-secondary-strong); }
|
|
21
|
+
.badge-success { --badge-color-bg: var(--color-success-muted); --badge-color-text: var(--color-success-strong); }
|
|
22
|
+
.badge-warning { --badge-color-bg: var(--color-warning-muted); --badge-color-text: var(--color-warning-strong); }
|
|
23
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/* components/banner.css */
|
|
2
|
+
@layer components {
|
|
3
|
+
.banner {
|
|
4
|
+
background-color: var(--alert-color-bg);
|
|
5
|
+
border-radius: var(--alert-border-radius);
|
|
6
|
+
color: var(--alert-color-text);
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
font-size: var(--alert-font-size);
|
|
10
|
+
gap: var(--alert-gap);
|
|
11
|
+
padding: var(--alert-padding);
|
|
12
|
+
padding-inline: 0;
|
|
13
|
+
text-align: center;
|
|
14
|
+
|
|
15
|
+
p {
|
|
16
|
+
margin: 0 auto;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Variants */
|
|
21
|
+
.banner-danger { --alert-color-bg: var(--color-danger-muted); --alert-color-text: var(--color-danger-strong); }
|
|
22
|
+
.banner-dark { --alert-color-bg: var(--color-dark-muted); --alert-color-text: var(--color-dark-strong); }
|
|
23
|
+
.banner-info { --alert-color-bg: var(--color-info-muted); --alert-color-text: var(--color-info-strong); }
|
|
24
|
+
.banner-light { --alert-color-bg: var(--color-light-muted); --alert-color-text: var(--color-light-strong); }
|
|
25
|
+
.banner-primary { --alert-color-bg: var(--color-primary-muted); --alert-color-text: var(--color-primary-strong); }
|
|
26
|
+
.banner-secondary { --alert-color-bg: var(--color-secondary-muted); --alert-color-text: var(--color-secondary-strong); }
|
|
27
|
+
.banner-success { --alert-color-bg: var(--color-success-muted); --alert-color-text: var(--color-success-strong); }
|
|
28
|
+
.banner-warning { --alert-color-bg: var(--color-warning-muted); --alert-color-text: var(--color-warning-strong); }
|
|
29
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* components/breadcrumb.css */
|
|
2
|
+
@layer components {
|
|
3
|
+
.breadcrumb-wrapper {
|
|
4
|
+
border-bottom: var(--breadcrumb-border-width) solid var(--breadcrumb-color-border);
|
|
5
|
+
border-top: var(--breadcrumb-border-width) solid var(--breadcrumb-color-border);
|
|
6
|
+
padding-block: var(--breadcrumb-padding);
|
|
7
|
+
|
|
8
|
+
ol {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-wrap: wrap;
|
|
11
|
+
font-size: var(--breadcrumb-font-size);
|
|
12
|
+
gap: var(--breadcrumb-gap);
|
|
13
|
+
list-style: none;
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
li {
|
|
19
|
+
align-items: center;
|
|
20
|
+
color: var(--breadcrumb-color-text);
|
|
21
|
+
display: flex;
|
|
22
|
+
gap: var(--breadcrumb-gap);
|
|
23
|
+
margin: 0;
|
|
24
|
+
|
|
25
|
+
a {
|
|
26
|
+
color: inherit;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&::before {
|
|
30
|
+
color: var(--breadcrumb-color-separator);
|
|
31
|
+
content: var(--breadcrumb-separator);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&:first-child::before {
|
|
35
|
+
display: none;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&[aria-current="page"] {
|
|
39
|
+
color: var(--breadcrumb-color-text-active);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* components/button.css */
|
|
2
|
+
@layer components {
|
|
3
|
+
.btn {
|
|
4
|
+
align-items: center;
|
|
5
|
+
appearance: none;
|
|
6
|
+
background-color: var(--btn-color-bg);
|
|
7
|
+
border: var(--btn-border-width) var(--btn-border-style) var(--btn-color-border);
|
|
8
|
+
border-radius: var(--btn-border-radius);
|
|
9
|
+
color: var(--btn-color-text);
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
display: inline-flex;
|
|
12
|
+
font-size: var(--btn-font-size);
|
|
13
|
+
font-weight: var(--btn-font-weight);
|
|
14
|
+
gap: var(--btn-gap);
|
|
15
|
+
justify-content: center;
|
|
16
|
+
padding: var(--btn-padding);
|
|
17
|
+
text-decoration-color: var(--btn-color-text-decoration);
|
|
18
|
+
text-decoration-line: var(--btn-text-decoration-line);
|
|
19
|
+
text-decoration-thickness: var(--text-decoration-thickness);
|
|
20
|
+
text-transform: var(--btn-text-transform);
|
|
21
|
+
text-underline-offset: var(--text-decoration-offset);
|
|
22
|
+
|
|
23
|
+
&:focus-visible {
|
|
24
|
+
outline: 2px solid var(--color-focus);
|
|
25
|
+
outline-offset: 2px;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
&:disabled,
|
|
29
|
+
&[aria-disabled='true'] {
|
|
30
|
+
cursor: not-allowed;
|
|
31
|
+
opacity: 0.5;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@media (hover: hover) and (pointer: fine) {
|
|
35
|
+
&:hover {
|
|
36
|
+
background-color: var(--btn-color-bg-hover);
|
|
37
|
+
color: var(--btn-color-text-hover);
|
|
38
|
+
text-decoration-color: var(--btn-color-text-decoration-hover);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
43
|
+
transition: var(--btn-transition);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Variants */
|
|
48
|
+
.btn-danger {
|
|
49
|
+
--btn-color-bg: var(--color-danger);
|
|
50
|
+
--btn-color-border: var(--color-danger);
|
|
51
|
+
--btn-color-text: var(--color-text-on-danger);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.btn-ghost {
|
|
55
|
+
--btn-color-bg: transparent;
|
|
56
|
+
--btn-color-border: transparent;
|
|
57
|
+
--btn-color-text: var(--color-text);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.btn-primary {
|
|
61
|
+
--btn-color-bg: var(--color-brand);
|
|
62
|
+
--btn-color-border: var(--color-brand);
|
|
63
|
+
--btn-color-text: var(--color-text-on-brand);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.btn-secondary {
|
|
67
|
+
--btn-color-bg: var(--color-secondary);
|
|
68
|
+
--btn-color-border: var(--color-secondary);
|
|
69
|
+
--btn-color-text: var(--color-text-on-secondary);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.btn-success {
|
|
73
|
+
--btn-color-bg: var(--color-success);
|
|
74
|
+
--btn-color-border: var(--color-success);
|
|
75
|
+
--btn-color-text: var(--color-text-on-success);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Sizes */
|
|
79
|
+
.btn-xs {
|
|
80
|
+
--btn-font-size: var(--font-size-xs);
|
|
81
|
+
--btn-padding: var(--spacing-xs);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.btn-sm {
|
|
85
|
+
--btn-font-size: var(--font-size-sm);
|
|
86
|
+
--btn-padding: var(--spacing-sm);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.btn-md {
|
|
90
|
+
--btn-font-size: var(--font-size-md);
|
|
91
|
+
--btn-padding: var(--spacing-md);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.btn-lg {
|
|
95
|
+
--btn-font-size: var(--font-size-lg);
|
|
96
|
+
--btn-padding: var(--spacing-lg);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.btn-xl {
|
|
100
|
+
--btn-font-size: var(--font-size-xl);
|
|
101
|
+
--btn-padding: var(--spacing-xl);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* components/card.css */
|
|
2
|
+
/*
|
|
3
|
+
* .card is a backwards-compatible alias for .item.
|
|
4
|
+
* Prefer using .item directly: <article class="item page">
|
|
5
|
+
*
|
|
6
|
+
* Structure:
|
|
7
|
+
* <article class="card">
|
|
8
|
+
* <div class="content">
|
|
9
|
+
* <p class="name">...</p>
|
|
10
|
+
* <p class="description">...</p>
|
|
11
|
+
* </div>
|
|
12
|
+
* <div class="media">
|
|
13
|
+
* picture | video
|
|
14
|
+
* </div>
|
|
15
|
+
* </article>
|
|
16
|
+
*/
|
|
17
|
+
@layer components {
|
|
18
|
+
.card {
|
|
19
|
+
background-color: var(--card-color-bg);
|
|
20
|
+
border: var(--card-border-width) solid var(--card-color-border);
|
|
21
|
+
border-radius: var(--card-border-radius);
|
|
22
|
+
box-shadow: var(--card-shadow);
|
|
23
|
+
color: var(--card-color-text);
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
|
|
28
|
+
.content {
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
flex: 1;
|
|
32
|
+
gap: var(--card-gap);
|
|
33
|
+
padding: var(--card-padding);
|
|
34
|
+
text-decoration: none;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.name {
|
|
38
|
+
color: var(--card-color-title);
|
|
39
|
+
font-size: var(--card-font-size-title);
|
|
40
|
+
font-weight: var(--card-font-weight-title);
|
|
41
|
+
line-height: var(--card-line-height-title);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.description {
|
|
45
|
+
color: var(--card-color-text-muted);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.media {
|
|
49
|
+
--media-ratio: var(--card-media-ratio);
|
|
50
|
+
--media-color-bg: var(--card-media-color-bg);
|
|
51
|
+
order: -1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/* components/dropdown.css */
|
|
2
|
+
/*
|
|
3
|
+
* .dropdown — contextual menu, vanilla JS driven.
|
|
4
|
+
*
|
|
5
|
+
* JS (components/dropdown.js) manages classes on .dropdown-menu:
|
|
6
|
+
* .showing — open transition starting
|
|
7
|
+
* .show — fully open
|
|
8
|
+
* .hiding — close transition starting
|
|
9
|
+
* aria-expanded on .js-dropdown-toggle
|
|
10
|
+
*
|
|
11
|
+
* Trigger: .js-dropdown-toggle (nextElementSibling must be .dropdown-menu)
|
|
12
|
+
*
|
|
13
|
+
* Expected HTML:
|
|
14
|
+
* <div class="dropdown">
|
|
15
|
+
* <button class="js-dropdown-toggle" aria-expanded="false">Label</button>
|
|
16
|
+
* <ul class="dropdown-menu" role="menu">
|
|
17
|
+
* <li><a class="dropdown-item" href="…">Item</a></li>
|
|
18
|
+
* <li><hr class="dropdown-divider"></li>
|
|
19
|
+
* <li><a class="dropdown-item" href="…">Item</a></li>
|
|
20
|
+
* </ul>
|
|
21
|
+
* </div>
|
|
22
|
+
*/
|
|
23
|
+
@layer components {
|
|
24
|
+
|
|
25
|
+
/* ── Container ───────────────────────────────────────────────────── */
|
|
26
|
+
|
|
27
|
+
.dropdown {
|
|
28
|
+
display: inline-flex;
|
|
29
|
+
position: relative;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* ── Trigger ─────────────────────────────────────────────────────── */
|
|
33
|
+
|
|
34
|
+
.dropdown-toggle {
|
|
35
|
+
align-items: center;
|
|
36
|
+
appearance: none;
|
|
37
|
+
border: 0;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
padding: var(--spacing-xs);
|
|
41
|
+
|
|
42
|
+
&::before {
|
|
43
|
+
content: '';
|
|
44
|
+
inset: 0;
|
|
45
|
+
position: absolute;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Chevron via CSS — rotate when open */
|
|
49
|
+
&::after {
|
|
50
|
+
border-left: 0.3em solid transparent;
|
|
51
|
+
border-right: 0.3em solid transparent;
|
|
52
|
+
border-top: 0.3em solid currentColor;
|
|
53
|
+
content: "";
|
|
54
|
+
display: inline-block;
|
|
55
|
+
transition: transform var(--dropdown-transition-duration) var(--dropdown-transition-easing);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&[aria-expanded="true"]::after {
|
|
59
|
+
transform: rotate(180deg);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* ── Menu panel ──────────────────────────────────────────────────── */
|
|
64
|
+
|
|
65
|
+
.dropdown-menu {
|
|
66
|
+
background-color: var(--dropdown-color-bg);
|
|
67
|
+
border: var(--dropdown-border-width) var(--dropdown-border-style) var(--dropdown-color-border);
|
|
68
|
+
border-radius: var(--dropdown-border-radius);
|
|
69
|
+
box-shadow: var(--dropdown-shadow);
|
|
70
|
+
color: var(--dropdown-color-text);
|
|
71
|
+
list-style: none;
|
|
72
|
+
min-width: var(--dropdown-min-width);
|
|
73
|
+
opacity: 0;
|
|
74
|
+
padding: var(--dropdown-padding, 0);
|
|
75
|
+
pointer-events: none;
|
|
76
|
+
position: absolute;
|
|
77
|
+
top: 100%;
|
|
78
|
+
transform: translateY(var(--dropdown-translate-y));
|
|
79
|
+
transition-duration: var(--dropdown-transition-duration);
|
|
80
|
+
transition-property: opacity, transform, visibility;
|
|
81
|
+
transition-timing-function: var(--dropdown-transition-easing);
|
|
82
|
+
visibility: hidden;
|
|
83
|
+
z-index: var(--dropdown-z-index);
|
|
84
|
+
|
|
85
|
+
/* Visible during all transition phases */
|
|
86
|
+
&.show,
|
|
87
|
+
&.showing,
|
|
88
|
+
&.hiding {
|
|
89
|
+
pointer-events: auto;
|
|
90
|
+
visibility: visible;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Fully open */
|
|
94
|
+
&.show {
|
|
95
|
+
opacity: 1;
|
|
96
|
+
transform: translateY(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* ── Items ───────────────────────────────────────────────────────── */
|
|
100
|
+
|
|
101
|
+
li a {
|
|
102
|
+
border-radius: var(--dropdown-item-border-radius);
|
|
103
|
+
color: var(--dropdown-color-text);
|
|
104
|
+
font-size: var(--dropdown-item-font-size);
|
|
105
|
+
display: block;
|
|
106
|
+
padding: var(--dropdown-item-padding);
|
|
107
|
+
text-decoration: none;
|
|
108
|
+
white-space: nowrap;
|
|
109
|
+
|
|
110
|
+
@media (hover: hover) and (pointer: fine) {
|
|
111
|
+
&:hover {
|
|
112
|
+
background-color: var(--dropdown-item-color-bg-hover);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
/* ── Alignment variants ──────────────────────────────────────────── */
|
|
120
|
+
|
|
121
|
+
.dropdown-menu-end {
|
|
122
|
+
left: auto;
|
|
123
|
+
right: 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* components/embed.css */
|
|
2
|
+
/*
|
|
3
|
+
* .embed — responsive iframe/embed container.
|
|
4
|
+
* Maintains aspect ratio for third-party embeds (video, map, social…).
|
|
5
|
+
*
|
|
6
|
+
* Structure:
|
|
7
|
+
* .embed
|
|
8
|
+
* iframe | video | embed | object
|
|
9
|
+
*/
|
|
10
|
+
@layer components {
|
|
11
|
+
.embed {
|
|
12
|
+
aspect-ratio: var(--embed-ratio);
|
|
13
|
+
border: var(--embed-border-width) var(--embed-border-style) var(--embed-color-border);
|
|
14
|
+
border-radius: var(--embed-border-radius);
|
|
15
|
+
box-shadow: var(--embed-box-shadow);
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
|
|
18
|
+
iframe,
|
|
19
|
+
embed,
|
|
20
|
+
object,
|
|
21
|
+
video {
|
|
22
|
+
height: 100%;
|
|
23
|
+
width: 100%;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/* components/list.css */
|
|
2
|
+
/*
|
|
3
|
+
* .list — compact labelled list with dash markers.
|
|
4
|
+
*
|
|
5
|
+
* Structure:
|
|
6
|
+
* div.list
|
|
7
|
+
* p (optional label)
|
|
8
|
+
* ul | ol
|
|
9
|
+
* li
|
|
10
|
+
*/
|
|
11
|
+
@layer components {
|
|
12
|
+
.list {
|
|
13
|
+
p {
|
|
14
|
+
font-weight: var(--list-label-font-weight);
|
|
15
|
+
margin-bottom: var(--list-label-margin-bottom);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
&,
|
|
19
|
+
ul,
|
|
20
|
+
ol {
|
|
21
|
+
list-style: none;
|
|
22
|
+
margin-bottom: 0;
|
|
23
|
+
padding-inline-start: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
li {
|
|
27
|
+
font-size: var(--list-item-font-size);
|
|
28
|
+
margin-block: var(--list-item-gap);
|
|
29
|
+
|
|
30
|
+
&::before {
|
|
31
|
+
content: var(--list-item-marker);
|
|
32
|
+
margin-inline-end: var(--list-item-marker-gap);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* components/map.css */
|
|
2
|
+
/*
|
|
3
|
+
* .map — map container, Leaflet-powered via js-map.
|
|
4
|
+
*
|
|
5
|
+
* Structure:
|
|
6
|
+
* div.map.js-map[data-markers][data-zoom][data-tile][data-marker-hidden]
|
|
7
|
+
*/
|
|
8
|
+
@layer components {
|
|
9
|
+
.map {
|
|
10
|
+
aspect-ratio: var(--map-ratio);
|
|
11
|
+
background-color: var(--map-color-bg);
|
|
12
|
+
margin-block: var(--map-margin-block);
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
|
|
15
|
+
@media (--mobile-only) {
|
|
16
|
+
--map-ratio: var(--map-ratio-mobile);
|
|
17
|
+
.container & {
|
|
18
|
+
margin-inline: calc(-1 * var(--gutter));
|
|
19
|
+
}
|
|
20
|
+
.row {
|
|
21
|
+
display: block;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/* components/media.css */
|
|
2
|
+
/*
|
|
3
|
+
* .media — structural base for all media components.
|
|
4
|
+
* .media-logo — logo media component.
|
|
5
|
+
* .media-icon — icon media component.
|
|
6
|
+
*/
|
|
7
|
+
@layer components {
|
|
8
|
+
|
|
9
|
+
.media {
|
|
10
|
+
aspect-ratio: var(--media-ratio);
|
|
11
|
+
background-color: var(--media-color-bg);
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
|
|
14
|
+
picture,
|
|
15
|
+
img {
|
|
16
|
+
height: 100%;
|
|
17
|
+
object-fit: cover;
|
|
18
|
+
width: 100%;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.media-logo {
|
|
23
|
+
picture {
|
|
24
|
+
align-items: center;
|
|
25
|
+
display: flex;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
}
|
|
28
|
+
img {
|
|
29
|
+
max-height: 70%;
|
|
30
|
+
max-width: 60%;
|
|
31
|
+
object-fit: contain;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.media-icon {
|
|
36
|
+
--media-ratio: none;
|
|
37
|
+
--media-color-bg: transparent;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/* components/nav-accessibility.css */
|
|
2
|
+
/*
|
|
3
|
+
* .nav-accessibility — skip navigation links for keyboard/AT users.
|
|
4
|
+
*
|
|
5
|
+
* Hidden off-screen by default; slides in when a child receives focus.
|
|
6
|
+
* Must be the first focusable element in <body>.
|
|
7
|
+
*
|
|
8
|
+
* Expected HTML (partials/nav/accessibility.html):
|
|
9
|
+
* <ul class="nav nav-accessibility">
|
|
10
|
+
* <li><a href="#main">…</a></li>
|
|
11
|
+
* <li><a href="#navigation">…</a></li>
|
|
12
|
+
* <li><a href="#footer">…</a></li>
|
|
13
|
+
* </ul>
|
|
14
|
+
*/
|
|
15
|
+
@layer components {
|
|
16
|
+
.nav-accessibility {
|
|
17
|
+
flex-direction: row;
|
|
18
|
+
font-size: var(--font-size-xs);
|
|
19
|
+
left: 0;
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
position: absolute;
|
|
22
|
+
transform: translateY(-100%);
|
|
23
|
+
transition: transform var(--duration-fast) var(--easing-out);
|
|
24
|
+
z-index: 300;
|
|
25
|
+
|
|
26
|
+
&:focus-within {
|
|
27
|
+
transform: translateY(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
a {
|
|
31
|
+
background: var(--color-bg);
|
|
32
|
+
&:focus {
|
|
33
|
+
background: var(--color-text);
|
|
34
|
+
color: var(--color-bg);
|
|
35
|
+
outline: none;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* components/nav.css */
|
|
2
|
+
/*
|
|
3
|
+
* .nav — base navigation list, vertical by default.
|
|
4
|
+
*
|
|
5
|
+
* Variants:
|
|
6
|
+
* .nav-title — bold group heading (not a link)
|
|
7
|
+
* .nav-social — icon-only social links
|
|
8
|
+
* .nav-language — compact language switcher
|
|
9
|
+
*
|
|
10
|
+
* Structure:
|
|
11
|
+
* <nav>
|
|
12
|
+
* <ul>
|
|
13
|
+
* <li>
|
|
14
|
+
* <a href="...">...</a>
|
|
15
|
+
* </li>
|
|
16
|
+
* </ul>
|
|
17
|
+
* </nav>
|
|
18
|
+
*
|
|
19
|
+
* Direction is set by context (e.g. header sets flex-direction: row).
|
|
20
|
+
*/
|
|
21
|
+
@layer components {
|
|
22
|
+
.nav {
|
|
23
|
+
ul,
|
|
24
|
+
ol {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
gap: var(--nav-gap);
|
|
28
|
+
list-style: none;
|
|
29
|
+
margin: 0;
|
|
30
|
+
padding: 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* ── Nav item ────────────────────────────────────────────────── */
|
|
34
|
+
|
|
35
|
+
li {
|
|
36
|
+
position: relative;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* ── Links ───────────────────────────────────────────────────── */
|
|
40
|
+
|
|
41
|
+
a {
|
|
42
|
+
display: block;
|
|
43
|
+
padding: var(--nav-padding-y) var(--nav-padding-x);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
a:not(.btn) {
|
|
47
|
+
color: var(--nav-color-text);
|
|
48
|
+
&.active {
|
|
49
|
+
--nav-color-text: var(--nav-color-text-active);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Non-link items (current page rendered as span) */
|
|
54
|
+
li > span {
|
|
55
|
+
color: var(--nav-color-text);
|
|
56
|
+
display: block;
|
|
57
|
+
padding: var(--nav-padding-y) var(--nav-padding-x);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* ── Nav title ───────────────────────────────────────────────────── */
|
|
62
|
+
|
|
63
|
+
.nav-title {
|
|
64
|
+
font-size: var(--nav-font-size-title);
|
|
65
|
+
font-weight: var(--nav-font-weight-title);
|
|
66
|
+
margin: 0;
|
|
67
|
+
padding: var(--nav-padding-y) var(--nav-padding-x);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.nav-item-highlighted {
|
|
71
|
+
line-height: 1.2;
|
|
72
|
+
margin-inline: var(--nav-padding-x);
|
|
73
|
+
&:first-child {
|
|
74
|
+
margin-inline-start: 0;
|
|
75
|
+
}
|
|
76
|
+
&:last-child {
|
|
77
|
+
margin-inline-end: 0;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/* components/offcanvas.css */
|
|
2
|
+
/*
|
|
3
|
+
* .offcanvas — slide-in panel, vanilla JS driven.
|
|
4
|
+
*
|
|
5
|
+
* JS (components/offcanvas.js) manages:
|
|
6
|
+
* .showing — panel becoming visible (open transition)
|
|
7
|
+
* .show — panel fully open
|
|
8
|
+
* .hiding — panel becoming hidden (close transition)
|
|
9
|
+
* aria-hidden="true" when closed
|
|
10
|
+
* body.offcanvas-open when panel is open
|
|
11
|
+
*
|
|
12
|
+
* Triggers: .js-offcanvas-toggle[data-target="#id"]
|
|
13
|
+
* Close: .js-offcanvas-close (inside panel)
|
|
14
|
+
*
|
|
15
|
+
* Position variants: .offcanvas-start | .offcanvas-end | .offcanvas-top | .offcanvas-bottom
|
|
16
|
+
*
|
|
17
|
+
* Expected HTML:
|
|
18
|
+
* <div id="navigation" class="offcanvas offcanvas-start" role="dialog" aria-modal="true" aria-hidden="true">
|
|
19
|
+
* <div class="header">
|
|
20
|
+
* <span class="title">...</span>
|
|
21
|
+
* <button class="js-offcanvas-close btn">...</button>
|
|
22
|
+
* </div>
|
|
23
|
+
* <div class="content">...</div>
|
|
24
|
+
* </div>
|
|
25
|
+
*/
|
|
26
|
+
@layer components {
|
|
27
|
+
|
|
28
|
+
/* ── Panel ───────────────────────────────────────────────────────── */
|
|
29
|
+
|
|
30
|
+
.offcanvas {
|
|
31
|
+
background-color: var(--offcanvas-color-bg);
|
|
32
|
+
bottom: var(--offcanvas-bottom, 0);
|
|
33
|
+
box-shadow: var(--offcanvas-shadow);
|
|
34
|
+
color: var(--offcanvas-color-text);
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
inset-inline-start: var(--offcanvas-start);
|
|
38
|
+
inset-inline-end: var(--offcanvas-end);
|
|
39
|
+
max-width: 100%;
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
position: fixed;
|
|
42
|
+
top: var(--offcanvas-top, 0);
|
|
43
|
+
transform: var(--offcanvas-translate);
|
|
44
|
+
transition-duration: var(--offcanvas-transition-duration);
|
|
45
|
+
transition-property: transform, visibility;
|
|
46
|
+
transition-timing-function: var(--offcanvas-transition-easing);
|
|
47
|
+
visibility: hidden;
|
|
48
|
+
width: var(--offcanvas-width);
|
|
49
|
+
z-index: var(--offcanvas-z-index);
|
|
50
|
+
|
|
51
|
+
/* Visible during transitions and when open */
|
|
52
|
+
&.show,
|
|
53
|
+
&.showing,
|
|
54
|
+
&.hiding {
|
|
55
|
+
visibility: visible;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Fully open — remove transform */
|
|
59
|
+
&.show {
|
|
60
|
+
transform: none;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* ── Header ────────────────────────────────────────────────────── */
|
|
64
|
+
|
|
65
|
+
& > .header {
|
|
66
|
+
align-items: center;
|
|
67
|
+
border-bottom: 1px solid var(--offcanvas-color-border);
|
|
68
|
+
display: flex;
|
|
69
|
+
flex-shrink: 0;
|
|
70
|
+
justify-content: space-between;
|
|
71
|
+
padding: var(--offcanvas-padding);
|
|
72
|
+
|
|
73
|
+
& .title {
|
|
74
|
+
font-size: var(--font-size-md);
|
|
75
|
+
font-weight: var(--font-weight-heading);
|
|
76
|
+
margin: 0;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* ── Body ──────────────────────────────────────────────────────── */
|
|
81
|
+
|
|
82
|
+
& > .content {
|
|
83
|
+
flex-grow: 1;
|
|
84
|
+
overflow-y: auto;
|
|
85
|
+
padding: var(--offcanvas-padding);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* ── Footer ──────────────────────────────────────────────────────── */
|
|
89
|
+
|
|
90
|
+
& > .footer {
|
|
91
|
+
flex-shrink: 0;
|
|
92
|
+
padding: var(--offcanvas-padding);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ── Position variants ───────────────────────────────────────────── */
|
|
97
|
+
|
|
98
|
+
.offcanvas-start {
|
|
99
|
+
--offcanvas-end: auto;
|
|
100
|
+
--offcanvas-start: 0;
|
|
101
|
+
--offcanvas-translate: translateX(-100%);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.offcanvas-end {
|
|
105
|
+
--offcanvas-start: auto;
|
|
106
|
+
--offcanvas-end: 0;
|
|
107
|
+
--offcanvas-translate: translateX(100%);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.offcanvas-top {
|
|
111
|
+
--offcanvas-end: 0;
|
|
112
|
+
--offcanvas-start: 0;
|
|
113
|
+
--offcanvas-top: 0;
|
|
114
|
+
--offcanvas-translate: translateY(-100%);
|
|
115
|
+
height: var(--offcanvas-height);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.offcanvas-bottom {
|
|
119
|
+
--offcanvas-end: 0;
|
|
120
|
+
--offcanvas-start: 0;
|
|
121
|
+
--offcanvas-bottom: 0;
|
|
122
|
+
--offcanvas-translate: translateY(100%);
|
|
123
|
+
height: var(--offcanvas-height);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/* ── Responsive variants (static above breakpoint) ──────────────── */
|
|
127
|
+
|
|
128
|
+
.offcanvas-tablet {
|
|
129
|
+
@media (--tablet) {
|
|
130
|
+
box-shadow: none;
|
|
131
|
+
overflow: visible;
|
|
132
|
+
position: static;
|
|
133
|
+
transform: none;
|
|
134
|
+
transition: none;
|
|
135
|
+
visibility: visible;
|
|
136
|
+
width: auto;
|
|
137
|
+
z-index: auto;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.offcanvas-laptop {
|
|
142
|
+
@media (--laptop) {
|
|
143
|
+
box-shadow: none;
|
|
144
|
+
overflow: visible;
|
|
145
|
+
position: static;
|
|
146
|
+
transform: none;
|
|
147
|
+
transition: none;
|
|
148
|
+
visibility: visible;
|
|
149
|
+
width: auto;
|
|
150
|
+
z-index: auto;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* ── Backdrop ────────────────────────────────────────────────────── */
|
|
155
|
+
|
|
156
|
+
.offcanvas-backdrop {
|
|
157
|
+
background-color: var(--offcanvas-backdrop-color);
|
|
158
|
+
inset: 0;
|
|
159
|
+
position: fixed;
|
|
160
|
+
z-index: calc(var(--offcanvas-z-index) - 1);
|
|
161
|
+
|
|
162
|
+
&.fade {
|
|
163
|
+
opacity: 0;
|
|
164
|
+
transition: opacity var(--offcanvas-transition-duration) var(--offcanvas-transition-easing);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
&.show {
|
|
168
|
+
opacity: 1;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* ── Body scroll lock ────────────────────────────────────────────── */
|
|
173
|
+
|
|
174
|
+
body.offcanvas-open {
|
|
175
|
+
overflow: hidden;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/* components/pagination.css */
|
|
2
|
+
/*
|
|
3
|
+
* .pagination — page navigation list.
|
|
4
|
+
*
|
|
5
|
+
* Structure:
|
|
6
|
+
* nav[role="navigation"]
|
|
7
|
+
* ul.pagination
|
|
8
|
+
* li[.disabled]
|
|
9
|
+
* a.first | a.previous | a.next | a.last (nav controls)
|
|
10
|
+
* li.item[.item-adjacent][.active]
|
|
11
|
+
* a (page number)
|
|
12
|
+
*
|
|
13
|
+
* Icon glyphs for .first, .last, .previous, .next are defined by the theme
|
|
14
|
+
* via a::before / a::after content.
|
|
15
|
+
*/
|
|
16
|
+
@layer components {
|
|
17
|
+
.pagination {
|
|
18
|
+
align-items: center;
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-wrap: wrap;
|
|
21
|
+
gap: var(--pagination-gap);
|
|
22
|
+
list-style: none;
|
|
23
|
+
margin-block: 0;
|
|
24
|
+
padding-inline-start: 0;
|
|
25
|
+
|
|
26
|
+
li {
|
|
27
|
+
margin-block: 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
a {
|
|
31
|
+
align-items: center;
|
|
32
|
+
background-color: var(--pagination-item-color-bg);
|
|
33
|
+
border-radius: var(--pagination-item-border-radius);
|
|
34
|
+
color: var(--pagination-item-color-text);
|
|
35
|
+
display: flex;
|
|
36
|
+
font-size: var(--pagination-item-font-size);
|
|
37
|
+
font-weight: var(--pagination-item-font-weight);
|
|
38
|
+
height: var(--pagination-item-size);
|
|
39
|
+
justify-content: center;
|
|
40
|
+
min-width: var(--pagination-item-size);
|
|
41
|
+
padding-inline: var(--spacing-2xs);
|
|
42
|
+
text-decoration: none;
|
|
43
|
+
|
|
44
|
+
&:hover {
|
|
45
|
+
background-color: var(--pagination-item-color-bg-hover);
|
|
46
|
+
color: var(--pagination-item-color-text-hover);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* Active page */
|
|
51
|
+
.active a {
|
|
52
|
+
--pagination-item-color-bg: var(--pagination-item-color-bg-active);
|
|
53
|
+
--pagination-item-color-text: var(--pagination-item-color-text-active);
|
|
54
|
+
cursor: default;
|
|
55
|
+
pointer-events: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Disabled nav control */
|
|
59
|
+
.disabled a {
|
|
60
|
+
opacity: var(--pagination-disabled-opacity);
|
|
61
|
+
pointer-events: none;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* On small screens: hide first/last controls and non-adjacent page numbers */
|
|
65
|
+
@media (--mobile-only) {
|
|
66
|
+
.first,
|
|
67
|
+
.last,
|
|
68
|
+
li:has(.first),
|
|
69
|
+
li:has(.last),
|
|
70
|
+
.item:not(.item-adjacent):not(.active) {
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
package/css/index.css
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* css/index.css */
|
|
2
|
+
@import 'component/alert.css';
|
|
3
|
+
@import 'component/badge.css';
|
|
4
|
+
@import 'component/banner.css';
|
|
5
|
+
@import 'component/breadcrumb.css';
|
|
6
|
+
@import 'component/button.css';
|
|
7
|
+
@import 'component/card.css';
|
|
8
|
+
@import 'component/dropdown.css';
|
|
9
|
+
@import 'component/embed.css';
|
|
10
|
+
@import 'component/list.css';
|
|
11
|
+
@import 'component/map.css';
|
|
12
|
+
@import 'component/media.css';
|
|
13
|
+
@import 'component/nav.css';
|
|
14
|
+
@import 'component/nav-accessibility.css';
|
|
15
|
+
@import 'component/offcanvas.css';
|
|
16
|
+
@import 'component/pagination.css';
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uncinq/css-components",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Framework-agnostic CSS component implementations.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/uncinq/css-components.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/uncinq/css-components#readme",
|
|
11
|
+
"author": {
|
|
12
|
+
"name": "Un Cinq",
|
|
13
|
+
"url": "https://uncinq.dev/"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"css",
|
|
17
|
+
"components",
|
|
18
|
+
"design-system",
|
|
19
|
+
"cascade-layers"
|
|
20
|
+
],
|
|
21
|
+
"files": [
|
|
22
|
+
"css/"
|
|
23
|
+
],
|
|
24
|
+
"exports": {
|
|
25
|
+
".": "./css/index.css",
|
|
26
|
+
"./css/index.css": "./css/index.css",
|
|
27
|
+
"./css/component/*": "./css/component/*"
|
|
28
|
+
},
|
|
29
|
+
"style": "./css/index.css",
|
|
30
|
+
"peerDependencies": {
|
|
31
|
+
"@uncinq/design-tokens": "*",
|
|
32
|
+
"@uncinq/component-tokens": "*"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
}
|
|
37
|
+
}
|