@grantcodes/ui 2.5.1 → 2.7.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/CHANGELOG.md +52 -0
- package/custom-elements.json +617 -19
- package/package.json +4 -4
- package/src/components/accordion/accordion.component.js +4 -1
- package/src/components/accordion/accordion.css +26 -18
- package/src/components/app-bar/app-bar.component.js +45 -9
- package/src/components/app-bar/app-bar.css +119 -88
- package/src/components/app-bar/app-bar.stories.js +75 -37
- package/src/components/app-bar/app-bar.test.js +7 -1
- package/src/components/app-bar/nav-link.component.js +15 -0
- package/src/components/app-bar/nav-link.css +58 -0
- package/src/components/app-bar/nav-link.js +6 -0
- package/src/components/app-bar/nav-link.react.js +9 -0
- package/src/components/code-preview/code-preview.css +5 -0
- package/src/components/container/container.css +6 -0
- package/src/components/countdown/countdown.component.js +180 -0
- package/src/components/countdown/countdown.css +62 -0
- package/src/components/countdown/countdown.js +6 -0
- package/src/components/countdown/countdown.react.js +9 -0
- package/src/components/countdown/countdown.stories.js +65 -0
- package/src/components/countdown/index.js +1 -0
- package/src/components/cta/cta.css +6 -0
- package/src/components/dialog/dialog.css +5 -0
- package/src/components/feature-list/feature-list.css +6 -0
- package/src/components/footer/footer.css +3 -1
- package/src/components/form-field/form-field.css +6 -0
- package/src/components/gallery/gallery.css +5 -0
- package/src/components/hero/hero.component.js +7 -0
- package/src/components/hero/hero.css +18 -1
- package/src/components/hero/hero.stories.js +30 -0
- package/src/components/icon/icon.css +6 -0
- package/src/components/loading/loading.css +5 -0
- package/src/components/logo-cloud/logo-cloud.css +6 -0
- package/src/components/map/index.js +1 -0
- package/src/components/map/map.component.js +135 -0
- package/src/components/map/map.css +41 -0
- package/src/components/map/map.js +6 -0
- package/src/components/map/map.react.js +9 -0
- package/src/components/map/map.stories.js +68 -0
- package/src/components/media-text/media-text.css +6 -0
- package/src/components/newsletter/newsletter.css +6 -0
- package/src/components/notice/notice.css +5 -0
- package/src/components/pagination/pagination.css +5 -0
- package/src/components/pricing/pricing.css +6 -0
- package/src/components/stats/stats.css +6 -0
- package/src/components/testimonials/testimonials.css +6 -0
- package/src/components/tooltip/tooltip.css +5 -0
- package/src/css/all.css +3 -1
- package/src/css/base.css +6 -247
- package/src/css/elements/a.css +8 -8
- package/src/css/reset.css +246 -0
- package/src/lib/styles/all.css +2 -0
- package/src/main.js +2 -0
- package/src/pages/blog-post.stories.js +7 -19
- package/src/react.js +3 -0
- package/src/types.d.ts +18 -0
|
@@ -22,7 +22,10 @@ export class GrantCodesAccordion extends LitElement {
|
|
|
22
22
|
(item, index) => html`
|
|
23
23
|
<details class="accordion__item">
|
|
24
24
|
<summary class="accordion__summary focus-ring">
|
|
25
|
-
|
|
25
|
+
<span>${item.title}</span>
|
|
26
|
+
<svg class="accordion__chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
27
|
+
<polyline points="6 9 12 15 18 9"></polyline>
|
|
28
|
+
</svg>
|
|
26
29
|
</summary>
|
|
27
30
|
<div class="accordion__content">${item.content}</div>
|
|
28
31
|
</details>
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
*,
|
|
2
|
+
*::before,
|
|
3
|
+
*::after {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
1
7
|
:host {
|
|
2
8
|
display: block;
|
|
3
9
|
}
|
|
@@ -9,51 +15,53 @@
|
|
|
9
15
|
}
|
|
10
16
|
|
|
11
17
|
.accordion__item {
|
|
12
|
-
border: 1px solid var(--g-theme-color-border-default);
|
|
18
|
+
border: 1px solid var(--g-theme-color-border-subtle, var(--g-theme-color-border-default));
|
|
13
19
|
border-radius: var(--g-theme-border-radius-md, 0.5rem);
|
|
14
|
-
height: 3.5rem;
|
|
15
|
-
transition: height 0.25s;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
.accordion__item[open] {
|
|
19
|
-
height: auto;
|
|
20
|
-
overflow: clip;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
.accordion__summary {
|
|
24
23
|
padding: var(--g-theme-spacing-md);
|
|
25
24
|
cursor: pointer;
|
|
26
25
|
background: var(--g-theme-color-background-subtle);
|
|
27
|
-
border: var(--g-theme-border-width-md) solid
|
|
28
|
-
var(--g-theme-color-border-subtle);
|
|
29
|
-
border-radius: var(--g-theme-border-radius-md);
|
|
30
26
|
font-weight: var(--g-typography-font-weight-500);
|
|
31
27
|
list-style: none;
|
|
32
28
|
display: flex;
|
|
33
29
|
justify-content: space-between;
|
|
34
30
|
align-items: center;
|
|
31
|
+
gap: var(--g-theme-spacing-md);
|
|
32
|
+
border-radius: var(--g-theme-border-radius-md, 0.5rem);
|
|
35
33
|
transition-property: background-color, outline-width, outline-color;
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
.accordion__summary:hover {
|
|
39
|
-
background: var(--g-theme-color-background-subtle-hover);
|
|
37
|
+
background: var(--g-theme-color-background-subtle-hover, var(--g-theme-color-background-subtle));
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
.accordion__summary::-webkit-details-marker {
|
|
43
41
|
display: none;
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
.accordion__chevron {
|
|
45
|
+
display: block;
|
|
46
|
+
inline-size: 1em;
|
|
47
|
+
block-size: 1em;
|
|
48
|
+
flex-shrink: 0;
|
|
49
|
+
transition: transform 0.25s ease;
|
|
50
|
+
color: var(--g-theme-color-content-secondary, currentColor);
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
.accordion__item[open] .
|
|
53
|
-
|
|
53
|
+
.accordion__item[open] .accordion__chevron {
|
|
54
|
+
transform: rotateX(180deg);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
.accordion__content {
|
|
57
58
|
padding: var(--g-theme-spacing-md);
|
|
58
59
|
background: var(--g-theme-color-background-default);
|
|
60
|
+
border-radius: 0 0 var(--g-theme-border-radius-md, 0.5rem) var(--g-theme-border-radius-md, 0.5rem);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@media (prefers-reduced-motion: reduce) {
|
|
64
|
+
.accordion__chevron {
|
|
65
|
+
transition: none;
|
|
66
|
+
}
|
|
59
67
|
}
|
|
@@ -33,6 +33,30 @@ export class GrantCodesAppBar extends LitElement {
|
|
|
33
33
|
* @type {boolean}
|
|
34
34
|
*/
|
|
35
35
|
this._mobileMenuOpen = false;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Close mobile menu when the component crosses the desktop breakpoint.
|
|
39
|
+
* Matches the 768px container query in app-bar.css.
|
|
40
|
+
*/
|
|
41
|
+
this._resizeObserver = typeof ResizeObserver !== "undefined"
|
|
42
|
+
? new ResizeObserver((entries) => {
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
if (entry.contentBoxSize[0].inlineSize >= 768 && this._mobileMenuOpen) {
|
|
45
|
+
this._mobileMenuOpen = false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
: null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
connectedCallback() {
|
|
53
|
+
super.connectedCallback();
|
|
54
|
+
this._resizeObserver?.observe(this);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
disconnectedCallback() {
|
|
58
|
+
super.disconnectedCallback();
|
|
59
|
+
this._resizeObserver?.disconnect();
|
|
36
60
|
}
|
|
37
61
|
|
|
38
62
|
_toggleMobileMenu() {
|
|
@@ -53,13 +77,27 @@ export class GrantCodesAppBar extends LitElement {
|
|
|
53
77
|
"app-bar--transparent": this.transparent,
|
|
54
78
|
});
|
|
55
79
|
|
|
80
|
+
const navClasses = classMap({
|
|
81
|
+
"app-bar__nav": true,
|
|
82
|
+
"app-bar__nav--open": this._mobileMenuOpen,
|
|
83
|
+
});
|
|
84
|
+
|
|
56
85
|
return html`
|
|
57
86
|
<header class=${classes}>
|
|
58
|
-
<div class="app-
|
|
87
|
+
<div class="app-bar__bar">
|
|
59
88
|
<div class="app-bar__logo">
|
|
60
89
|
<slot name="logo"></slot>
|
|
61
90
|
</div>
|
|
62
91
|
|
|
92
|
+
<!-- Navigation (inline on desktop, overlay on mobile) -->
|
|
93
|
+
<nav
|
|
94
|
+
part="nav"
|
|
95
|
+
class=${navClasses}
|
|
96
|
+
aria-label="Main navigation"
|
|
97
|
+
>
|
|
98
|
+
<slot name="nav"></slot>
|
|
99
|
+
</nav>
|
|
100
|
+
|
|
63
101
|
<!-- Actions (right side) -->
|
|
64
102
|
<div class="app-bar__actions">
|
|
65
103
|
<slot name="actions"></slot>
|
|
@@ -75,15 +113,13 @@ export class GrantCodesAppBar extends LitElement {
|
|
|
75
113
|
>
|
|
76
114
|
<span class="app-bar__menu-icon"></span>
|
|
77
115
|
</button>
|
|
78
|
-
|
|
79
|
-
<!-- Navigation (single slot, collapsible on mobile) -->
|
|
80
|
-
<nav
|
|
81
|
-
class="app-bar__nav ${this._mobileMenuOpen ? "app-bar__nav--mobile-open" : ""}"
|
|
82
|
-
aria-label="Main navigation"
|
|
83
|
-
>
|
|
84
|
-
<slot name="nav"></slot>
|
|
85
|
-
</nav>
|
|
86
116
|
</div>
|
|
117
|
+
|
|
118
|
+
<!-- Mobile overlay backdrop -->
|
|
119
|
+
<div
|
|
120
|
+
class="app-bar__overlay ${this._mobileMenuOpen ? "app-bar__overlay--visible" : ""}"
|
|
121
|
+
@click=${this._toggleMobileMenu}
|
|
122
|
+
></div>
|
|
87
123
|
</header>
|
|
88
124
|
`;
|
|
89
125
|
}
|
|
@@ -13,9 +13,13 @@
|
|
|
13
13
|
.app-bar {
|
|
14
14
|
background-color: var(--g-theme-color-background-default);
|
|
15
15
|
border-block-end: 1px solid var(--g-theme-color-border-default);
|
|
16
|
+
position: relative;
|
|
17
|
+
z-index: 100;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.app-bar--sticky {
|
|
16
21
|
position: sticky;
|
|
17
22
|
inset-block-start: 0;
|
|
18
|
-
z-index: 100;
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
.app-bar--transparent {
|
|
@@ -24,7 +28,8 @@
|
|
|
24
28
|
box-shadow: none;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
/* Top bar row — always visible */
|
|
32
|
+
.app-bar__bar {
|
|
28
33
|
display: flex;
|
|
29
34
|
align-items: center;
|
|
30
35
|
gap: var(--g-theme-spacing-lg);
|
|
@@ -32,7 +37,6 @@
|
|
|
32
37
|
padding-block: var(--g-theme-spacing-md);
|
|
33
38
|
max-inline-size: 1400px;
|
|
34
39
|
margin-inline: auto;
|
|
35
|
-
flex-wrap: wrap;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
/* Logo */
|
|
@@ -42,39 +46,16 @@
|
|
|
42
46
|
flex-shrink: 0;
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
/* Navigation */
|
|
49
|
+
/* Navigation — hidden on mobile, shown on desktop */
|
|
46
50
|
.app-bar__nav {
|
|
47
51
|
display: none;
|
|
48
|
-
flex: 1;
|
|
49
|
-
align-items: center;
|
|
50
|
-
gap: var(--g-theme-spacing-lg);
|
|
51
|
-
order: 5;
|
|
52
|
-
inline-size: 100%;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/* Mobile: when menu is open, show nav stacked vertically */
|
|
56
|
-
.app-bar__nav--mobile-open {
|
|
57
|
-
display: flex;
|
|
58
|
-
flex-direction: column;
|
|
59
|
-
gap: var(--g-theme-spacing-sm);
|
|
60
|
-
padding-inline: var(--g-theme-spacing-md);
|
|
61
|
-
padding-block: var(--g-theme-spacing-md);
|
|
62
|
-
border-block-start: 1px solid var(--g-theme-color-border-default);
|
|
63
|
-
width: 100%;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/* Show nav on larger screens using container queries */
|
|
67
|
-
@container app-bar (min-width: 768px) {
|
|
68
|
-
.app-bar__nav {
|
|
69
|
-
display: flex;
|
|
70
|
-
}
|
|
71
52
|
}
|
|
72
53
|
|
|
73
54
|
/* Actions */
|
|
74
55
|
.app-bar__actions {
|
|
75
56
|
display: flex;
|
|
76
57
|
align-items: center;
|
|
77
|
-
gap: var(--g-spacing-
|
|
58
|
+
gap: var(--g-theme-spacing-sm);
|
|
78
59
|
margin-inline-start: auto;
|
|
79
60
|
}
|
|
80
61
|
|
|
@@ -84,8 +65,8 @@
|
|
|
84
65
|
display: flex;
|
|
85
66
|
align-items: center;
|
|
86
67
|
justify-content: center;
|
|
87
|
-
inline-size: 2.
|
|
88
|
-
block-size: 2.
|
|
68
|
+
inline-size: 2.75rem;
|
|
69
|
+
block-size: 2.75rem;
|
|
89
70
|
padding: var(--g-theme-spacing-sm);
|
|
90
71
|
border-radius: var(--g-theme-border-radius-md);
|
|
91
72
|
cursor: pointer;
|
|
@@ -97,18 +78,11 @@
|
|
|
97
78
|
background-color: var(--g-theme-color-background-subtle-hover);
|
|
98
79
|
}
|
|
99
80
|
|
|
100
|
-
/* Hide menu button on larger screens */
|
|
101
|
-
@container app-bar (min-width: 768px) {
|
|
102
|
-
.app-bar__menu-button {
|
|
103
|
-
display: none;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
81
|
/* Hamburger Icon */
|
|
108
82
|
.app-bar__menu-icon {
|
|
109
83
|
display: block;
|
|
110
|
-
inline-size: 1.
|
|
111
|
-
block-size:
|
|
84
|
+
inline-size: 1.25rem;
|
|
85
|
+
block-size: 1rem;
|
|
112
86
|
position: relative;
|
|
113
87
|
}
|
|
114
88
|
|
|
@@ -120,55 +94,130 @@
|
|
|
120
94
|
block-size: 2px;
|
|
121
95
|
inset-inline-start: 0;
|
|
122
96
|
background-color: currentColor;
|
|
123
|
-
transition: transform 0.2s ease;
|
|
97
|
+
transition: transform 0.2s ease, inset 0.2s ease;
|
|
124
98
|
}
|
|
125
99
|
|
|
126
100
|
.app-bar__menu-icon::before {
|
|
127
|
-
inset-block-start: 0
|
|
128
|
-
box-shadow: 0 0.
|
|
101
|
+
inset-block-start: 0;
|
|
102
|
+
box-shadow: 0 0.4375rem 0 currentColor;
|
|
129
103
|
}
|
|
130
104
|
|
|
131
105
|
.app-bar__menu-icon::after {
|
|
132
|
-
inset-block-end: 0
|
|
106
|
+
inset-block-end: 0;
|
|
133
107
|
}
|
|
134
108
|
|
|
135
109
|
/* Animated hamburger to X */
|
|
136
110
|
.app-bar__menu-button[aria-expanded="true"] .app-bar__menu-icon::before {
|
|
137
|
-
inset-block-start:
|
|
111
|
+
inset-block-start: calc(50% - 1px);
|
|
138
112
|
transform: rotate(45deg);
|
|
139
113
|
box-shadow: none;
|
|
140
114
|
}
|
|
141
115
|
|
|
142
116
|
.app-bar__menu-button[aria-expanded="true"] .app-bar__menu-icon::after {
|
|
143
|
-
inset-block-end:
|
|
117
|
+
inset-block-end: calc(50% - 1px);
|
|
144
118
|
transform: rotate(-45deg);
|
|
145
119
|
}
|
|
146
120
|
|
|
147
|
-
/*
|
|
148
|
-
|
|
121
|
+
/*
|
|
122
|
+
* Mobile menu — full-width panel below the app bar.
|
|
123
|
+
* Uses clip-path for a clean slide-down reveal animation.
|
|
124
|
+
*/
|
|
125
|
+
.app-bar__nav--open {
|
|
149
126
|
display: flex;
|
|
150
127
|
flex-direction: column;
|
|
151
|
-
gap: var(--g-theme-spacing-
|
|
152
|
-
|
|
128
|
+
gap: var(--g-theme-spacing-xs, 0.25rem);
|
|
129
|
+
position: absolute;
|
|
130
|
+
inset-inline: 0;
|
|
131
|
+
inset-block-start: 100%;
|
|
132
|
+
z-index: 99;
|
|
133
|
+
background-color: var(--g-theme-color-background-default);
|
|
134
|
+
border-block-end: 1px solid var(--g-theme-color-border-default);
|
|
153
135
|
padding-block: var(--g-theme-spacing-md);
|
|
154
|
-
|
|
155
|
-
|
|
136
|
+
padding-inline: var(--g-theme-spacing-md);
|
|
137
|
+
box-shadow: var(--g-theme-shadow-lg, 0 8px 24px rgba(0, 0, 0, 0.12));
|
|
138
|
+
animation: menu-open 0.25s ease-out;
|
|
156
139
|
}
|
|
157
140
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
141
|
+
@keyframes menu-open {
|
|
142
|
+
from {
|
|
143
|
+
opacity: 0;
|
|
144
|
+
transform: translateY(-0.5rem);
|
|
145
|
+
}
|
|
146
|
+
to {
|
|
147
|
+
opacity: 1;
|
|
148
|
+
transform: translateY(0);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Slotted nav content stacks vertically in mobile menu */
|
|
153
|
+
.app-bar__nav--open ::slotted(ul),
|
|
154
|
+
.app-bar__nav--open ::slotted(div[slot="nav"]) {
|
|
155
|
+
display: flex;
|
|
156
|
+
flex-direction: column;
|
|
157
|
+
gap: var(--g-theme-spacing-xs, 0.25rem);
|
|
158
|
+
margin: 0;
|
|
159
|
+
padding: 0;
|
|
160
|
+
list-style: none;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Backdrop overlay — covers viewport below app bar when menu is open */
|
|
164
|
+
.app-bar__overlay {
|
|
165
|
+
display: none;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.app-bar__overlay--visible {
|
|
169
|
+
display: block;
|
|
170
|
+
position: fixed;
|
|
171
|
+
inset: 0;
|
|
172
|
+
z-index: 98;
|
|
173
|
+
background-color: rgba(0, 0, 0, 0.2);
|
|
174
|
+
animation: overlay-fade 0.2s ease-out;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@keyframes overlay-fade {
|
|
178
|
+
from { opacity: 0; }
|
|
179
|
+
to { opacity: 1; }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Reduced motion preferences */
|
|
183
|
+
@media (prefers-reduced-motion: reduce) {
|
|
184
|
+
.app-bar__menu-icon::before,
|
|
185
|
+
.app-bar__menu-icon::after {
|
|
186
|
+
transition: none;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.app-bar__menu-button {
|
|
190
|
+
transition: none;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.app-bar__nav--open {
|
|
194
|
+
animation: none;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.app-bar__overlay--visible {
|
|
198
|
+
animation: none;
|
|
162
199
|
}
|
|
200
|
+
}
|
|
163
201
|
|
|
202
|
+
/* Desktop: show nav inline, hide hamburger */
|
|
203
|
+
@container app-bar (min-width: 768px) {
|
|
164
204
|
.app-bar__nav {
|
|
165
205
|
display: flex;
|
|
166
206
|
flex-direction: row;
|
|
167
|
-
|
|
168
|
-
|
|
207
|
+
align-items: center;
|
|
208
|
+
gap: var(--g-theme-spacing-xs, 0.25rem);
|
|
209
|
+
flex: 1;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Reset mobile-open styles at desktop — nav is always inline */
|
|
213
|
+
.app-bar__nav--open {
|
|
214
|
+
position: static;
|
|
215
|
+
background-color: transparent;
|
|
169
216
|
border: 0;
|
|
170
|
-
|
|
171
|
-
|
|
217
|
+
padding: 0;
|
|
218
|
+
box-shadow: none;
|
|
219
|
+
animation: none;
|
|
220
|
+
flex-direction: row;
|
|
172
221
|
}
|
|
173
222
|
|
|
174
223
|
.app-bar__actions {
|
|
@@ -176,46 +225,28 @@
|
|
|
176
225
|
}
|
|
177
226
|
|
|
178
227
|
.app-bar__menu-button {
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/* Ensure mobile-open class doesn't affect desktop */
|
|
183
|
-
.app-bar__nav--mobile-open {
|
|
184
|
-
display: flex;
|
|
228
|
+
display: none;
|
|
185
229
|
}
|
|
186
|
-
}
|
|
187
230
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
transform: translateY(-0.5rem);
|
|
192
|
-
}
|
|
193
|
-
to {
|
|
194
|
-
opacity: 1;
|
|
195
|
-
transform: translateY(0);
|
|
231
|
+
/* Overlay is never visible on desktop */
|
|
232
|
+
.app-bar__overlay--visible {
|
|
233
|
+
display: none;
|
|
196
234
|
}
|
|
197
235
|
}
|
|
198
236
|
|
|
199
|
-
/* Slotted content styling
|
|
200
|
-
::slotted(a) {
|
|
201
|
-
color: var(--g-theme-color-content-default);
|
|
202
|
-
text-decoration: none;
|
|
203
|
-
padding-block: var(--g-theme-spacing-sm);
|
|
204
|
-
padding-inline: var(--g-spacing-12);
|
|
205
|
-
border-radius: var(--g-theme-border-radius-md);
|
|
206
|
-
transition: all 0.2s ease;
|
|
237
|
+
/* Slotted content styling */
|
|
238
|
+
::slotted(a[slot="logo"]) {
|
|
239
|
+
color: var(--g-theme-color-content-default) !important;
|
|
240
|
+
text-decoration: none !important;
|
|
207
241
|
font-family: var(--g-theme-typography-label-font-family);
|
|
208
242
|
font-weight: var(--g-theme-typography-label-font-weight);
|
|
209
243
|
}
|
|
210
244
|
|
|
211
|
-
::slotted(
|
|
212
|
-
|
|
213
|
-
background-color: var(--g-theme-color-background-subtle-hover);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
::slotted(ul) {
|
|
245
|
+
::slotted(ul),
|
|
246
|
+
::slotted(div[slot="nav"]) {
|
|
217
247
|
display: flex;
|
|
218
248
|
flex-direction: row;
|
|
249
|
+
gap: var(--g-theme-spacing-xs, 0.25rem);
|
|
219
250
|
margin: 0;
|
|
220
251
|
padding: 0;
|
|
221
252
|
list-style: none;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { html } from "lit";
|
|
2
2
|
import "./app-bar.js";
|
|
3
|
+
import "./nav-link.js";
|
|
3
4
|
import "../button/button.js";
|
|
4
5
|
|
|
5
6
|
const meta = {
|
|
@@ -10,19 +11,18 @@ const meta = {
|
|
|
10
11
|
export default meta;
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
|
-
* Basic app bar with logo and
|
|
14
|
+
* Basic app bar with logo, navigation links, and a sign in action.
|
|
15
|
+
* Nav links use <grantcodes-nav-link> for consistent ghost button styling.
|
|
14
16
|
*/
|
|
15
|
-
export const
|
|
17
|
+
export const Default = {
|
|
16
18
|
render: () => html`
|
|
17
19
|
<grantcodes-app-bar>
|
|
18
|
-
<a slot="logo" href="/"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<a href="/">
|
|
23
|
-
<a href="/
|
|
24
|
-
<a href="/features">Features</a>
|
|
25
|
-
<a href="/pricing">Pricing</a>
|
|
20
|
+
<a slot="logo" href="/">MyApp</a>
|
|
21
|
+
<div slot="nav">
|
|
22
|
+
<grantcodes-nav-link><a href="/">Home</a></grantcodes-nav-link>
|
|
23
|
+
<grantcodes-nav-link><a href="/about">About</a></grantcodes-nav-link>
|
|
24
|
+
<grantcodes-nav-link><a href="/features">Features</a></grantcodes-nav-link>
|
|
25
|
+
<grantcodes-nav-link><a href="/pricing">Pricing</a></grantcodes-nav-link>
|
|
26
26
|
</div>
|
|
27
27
|
<div slot="actions">
|
|
28
28
|
<grantcodes-button>Sign In</grantcodes-button>
|
|
@@ -32,52 +32,90 @@ export const AppBar = {
|
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
35
|
+
* Navigation using a <ul> list — matches the real-world pattern from the Astro site.
|
|
36
|
+
* Each <li> wraps a <grantcodes-nav-link> so styling works through the shadow boundary.
|
|
37
|
+
*/
|
|
38
|
+
export const WithListNav = {
|
|
39
|
+
render: () => html`
|
|
40
|
+
<grantcodes-app-bar>
|
|
41
|
+
<a slot="logo" href="/">MyApp</a>
|
|
42
|
+
<ul slot="nav">
|
|
43
|
+
<li><grantcodes-nav-link><a href="/docs">Docs</a></grantcodes-nav-link></li>
|
|
44
|
+
<li><grantcodes-nav-link><a href="/blog">Blog</a></grantcodes-nav-link></li>
|
|
45
|
+
<li><grantcodes-nav-link><a href="/changelog">Changelog</a></grantcodes-nav-link></li>
|
|
46
|
+
</ul>
|
|
47
|
+
<div slot="actions">
|
|
48
|
+
<grantcodes-button>Sign In</grantcodes-button>
|
|
49
|
+
</div>
|
|
50
|
+
</grantcodes-app-bar>
|
|
51
|
+
`,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Sticky app bar that stays at the top of the viewport when scrolling.
|
|
56
|
+
*/
|
|
57
|
+
export const Sticky = {
|
|
58
|
+
render: () => html`
|
|
59
|
+
<div style="min-height: 200vh;">
|
|
60
|
+
<grantcodes-app-bar sticky>
|
|
61
|
+
<a slot="logo" href="/">MyApp</a>
|
|
62
|
+
<div slot="nav">
|
|
63
|
+
<grantcodes-nav-link><a href="/">Home</a></grantcodes-nav-link>
|
|
64
|
+
<grantcodes-nav-link><a href="/about">About</a></grantcodes-nav-link>
|
|
65
|
+
<grantcodes-nav-link><a href="/features">Features</a></grantcodes-nav-link>
|
|
66
|
+
<grantcodes-nav-link><a href="/pricing">Pricing</a></grantcodes-nav-link>
|
|
67
|
+
</div>
|
|
68
|
+
<div slot="actions">
|
|
69
|
+
<grantcodes-button>Sign In</grantcodes-button>
|
|
70
|
+
</div>
|
|
71
|
+
</grantcodes-app-bar>
|
|
72
|
+
<p style="padding: 2rem;">
|
|
73
|
+
Scroll down to see the sticky app bar remain at the top of the viewport.
|
|
74
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque
|
|
75
|
+
habitant morbi tristique senectus et netus et malesuada fames ac turpis
|
|
76
|
+
egestas.
|
|
77
|
+
</p>
|
|
78
|
+
</div>
|
|
79
|
+
`,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Transparent app bar for use over hero sections or colored backgrounds.
|
|
36
84
|
*/
|
|
37
85
|
export const Transparent = {
|
|
38
86
|
render: () => html`
|
|
39
87
|
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 400px;">
|
|
40
88
|
<grantcodes-app-bar transparent>
|
|
41
|
-
<a slot="logo" href="/"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
<a href="/"
|
|
46
|
-
<a href="/about" style="color: white;">About</a>
|
|
47
|
-
<a href="/features" style="color: white;">Features</a>
|
|
89
|
+
<a slot="logo" href="/">MyApp</a>
|
|
90
|
+
<div slot="nav">
|
|
91
|
+
<grantcodes-nav-link><a href="/">Home</a></grantcodes-nav-link>
|
|
92
|
+
<grantcodes-nav-link><a href="/about">About</a></grantcodes-nav-link>
|
|
93
|
+
<grantcodes-nav-link><a href="/features">Features</a></grantcodes-nav-link>
|
|
48
94
|
</div>
|
|
49
95
|
<div slot="actions">
|
|
50
|
-
<grantcodes-button
|
|
51
|
-
Sign Up
|
|
52
|
-
</grantcodes-button>
|
|
96
|
+
<grantcodes-button>Sign Up</grantcodes-button>
|
|
53
97
|
</div>
|
|
54
98
|
</grantcodes-app-bar>
|
|
55
|
-
<div style="padding: 4rem 2rem; text-align: center; color: white;">
|
|
56
|
-
<h1 style="font-size: 3rem; margin: 0;">Welcome to MyApp</h1>
|
|
57
|
-
<p style="font-size: 1.5rem; margin-top: 1rem;">The transparent app bar blends seamlessly</p>
|
|
58
|
-
</div>
|
|
59
99
|
</div>
|
|
60
100
|
`,
|
|
61
101
|
};
|
|
62
102
|
|
|
63
103
|
/**
|
|
64
|
-
* App bar with multiple
|
|
104
|
+
* App bar with multiple action buttons.
|
|
65
105
|
*/
|
|
66
106
|
export const WithMultipleActions = {
|
|
67
107
|
render: () => html`
|
|
68
108
|
<grantcodes-app-bar>
|
|
69
|
-
<a slot="logo" href="/"
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<a href="/">
|
|
74
|
-
<a href="/analytics">Analytics</a>
|
|
75
|
-
<a href="/reports">Reports</a>
|
|
109
|
+
<a slot="logo" href="/">Dashboard</a>
|
|
110
|
+
<div slot="nav">
|
|
111
|
+
<grantcodes-nav-link><a href="/">Overview</a></grantcodes-nav-link>
|
|
112
|
+
<grantcodes-nav-link><a href="/analytics">Analytics</a></grantcodes-nav-link>
|
|
113
|
+
<grantcodes-nav-link><a href="/reports">Reports</a></grantcodes-nav-link>
|
|
76
114
|
</div>
|
|
77
|
-
<div slot="actions"
|
|
78
|
-
<grantcodes-button
|
|
79
|
-
<grantcodes-button
|
|
80
|
-
<grantcodes-button
|
|
115
|
+
<div slot="actions">
|
|
116
|
+
<grantcodes-button>Notifications</grantcodes-button>
|
|
117
|
+
<grantcodes-button>Settings</grantcodes-button>
|
|
118
|
+
<grantcodes-button>Profile</grantcodes-button>
|
|
81
119
|
</div>
|
|
82
120
|
</grantcodes-app-bar>
|
|
83
121
|
`,
|
|
@@ -40,6 +40,12 @@ describe("App Bar Component", () => {
|
|
|
40
40
|
assert.ok(header, "Header element should exist");
|
|
41
41
|
});
|
|
42
42
|
|
|
43
|
+
it("should not have sticky class by default", async () => {
|
|
44
|
+
element = await fixture("grantcodes-app-bar");
|
|
45
|
+
const appBar = element.shadowRoot.querySelector(".app-bar--sticky");
|
|
46
|
+
assert.ok(!appBar, "Should not have sticky class by default");
|
|
47
|
+
});
|
|
48
|
+
|
|
43
49
|
it("should apply sticky class when sticky", async () => {
|
|
44
50
|
element = await fixture("grantcodes-app-bar", {
|
|
45
51
|
sticky: true,
|
|
@@ -108,7 +114,7 @@ describe("App Bar Component", () => {
|
|
|
108
114
|
await element.updateComplete;
|
|
109
115
|
|
|
110
116
|
const mobileNav = element.shadowRoot.querySelector(
|
|
111
|
-
".app-bar__nav--
|
|
117
|
+
".app-bar__nav--open",
|
|
112
118
|
);
|
|
113
119
|
assert.ok(mobileNav, "Mobile nav should be rendered when open");
|
|
114
120
|
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { LitElement, html } from "lit";
|
|
2
|
+
import focusRingStyles from "#styles/focus-ring.css" with { type: "css" };
|
|
3
|
+
import navLinkStyles from "./nav-link.css" with { type: "css" };
|
|
4
|
+
|
|
5
|
+
export class GrantCodesNavLink extends LitElement {
|
|
6
|
+
static styles = [focusRingStyles, navLinkStyles];
|
|
7
|
+
|
|
8
|
+
render() {
|
|
9
|
+
return html`
|
|
10
|
+
<span class="nav-link">
|
|
11
|
+
<slot></slot>
|
|
12
|
+
</span>
|
|
13
|
+
`;
|
|
14
|
+
}
|
|
15
|
+
}
|