@astronox/core 0.3.0 → 0.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astronox/core",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Reusable Astro components, patterns, styles, and infrastructure for AstroNox sites.",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -7,6 +7,7 @@ import Media from '../../../primitives/media/Media/Media.astro';
7
7
 
8
8
  type Props = Omit<HTMLAttributes<'button'>, 'type'> &
9
9
  Omit<HTMLAttributes<'a'>, 'href'> & {
10
+ collapse?: 'none' | 'icon';
10
11
  compactLabel?: string;
11
12
  disabled?: boolean;
12
13
  href?: string;
@@ -24,6 +25,8 @@ type Props = Omit<HTMLAttributes<'button'>, 'type'> &
24
25
  const {
25
26
  class: className,
26
27
  'data-tone': dataTone,
28
+ 'aria-label': ariaLabel,
29
+ collapse = 'none',
27
30
  compactLabel,
28
31
  disabled = false,
29
32
  href,
@@ -44,6 +47,8 @@ const resolvedRel =
44
47
  rel ?? (target === '_blank' ? 'noopener noreferrer' : undefined);
45
48
  const resolvedTone = tone ?? dataTone;
46
49
  const hasVisual = Boolean(Icon || media);
50
+ const resolvedAriaLabel =
51
+ ariaLabel ?? (collapse === 'icon' && hasVisual ? label : undefined);
47
52
  const isLink = Boolean(href);
48
53
  const isDisabledLink = isLink && disabled;
49
54
  const Root = isLink ? (isDisabledLink ? 'span' : 'a') : 'button';
@@ -51,8 +56,10 @@ const Root = isLink ? (isDisabledLink ? 'span' : 'a') : 'button';
51
56
 
52
57
  <Root
53
58
  {...attrs}
59
+ aria-label={resolvedAriaLabel}
54
60
  aria-disabled={isDisabledLink ? 'true' : undefined}
55
61
  class:list={['card-action', className]}
62
+ data-card-action-collapse={hasVisual ? collapse : undefined}
56
63
  data-card-action-compact-label={compactLabel ? true : undefined}
57
64
  data-card-action-hover-tone={hoverTone}
58
65
  data-card-action-label-treatment={labelTreatment}
@@ -7,6 +7,7 @@
7
7
  --card-action-icon-size: var(--nox-compact-media-responsive-size);
8
8
  --card-action-hover-seed: var(--nox-tone-seed);
9
9
 
10
+ position: relative;
10
11
  display: grid;
11
12
  min-width: 0;
12
13
  min-height: var(--card-action-size);
@@ -169,6 +170,31 @@
169
170
  }
170
171
 
171
172
  @media (width < 48rem) {
173
+ .card-action[data-card-action-collapse='icon'][data-has-visual] {
174
+ grid-template-columns: minmax(0, 1fr);
175
+ }
176
+
177
+ .card-action[data-card-action-collapse='icon'][data-has-visual]
178
+ .card-action__visual {
179
+ grid-column: 1;
180
+ inline-size: 100%;
181
+ border-inline: 0;
182
+ }
183
+
184
+ .card-action[data-card-action-collapse='icon'][data-has-visual]
185
+ .card-action__label {
186
+ position: absolute;
187
+ overflow: hidden;
188
+ width: 1px;
189
+ min-height: 0;
190
+ height: 1px;
191
+ margin: -1px;
192
+ padding: 0;
193
+ clip: rect(0 0 0 0);
194
+ clip-path: inset(50%);
195
+ white-space: nowrap;
196
+ }
197
+
172
198
  .card-action[data-card-action-compact-label] .card-action__label-full {
173
199
  display: none;
174
200
  }
@@ -54,7 +54,11 @@ function updateToggles(theme: ColorTheme): void {
54
54
  }
55
55
 
56
56
  function applyTheme(theme: ColorTheme, persist: boolean): void {
57
- document.documentElement.dataset.theme = theme;
57
+ const root = document.documentElement;
58
+ root.dataset.themeSwitching = 'true';
59
+ root.dataset.theme = theme;
60
+ void root.offsetWidth;
61
+ delete root.dataset.themeSwitching;
58
62
 
59
63
  if (persist) {
60
64
  try {
@@ -51,6 +51,8 @@ const fallbackItems = [...leftItems, ...rightItems].flatMap((item) =>
51
51
  const resolvedMobileItems = mobileItems ?? fallbackItems;
52
52
  const resolvedHasUtilityActions =
53
53
  hasUtilityActions ?? Astro.slots.has('utility-actions');
54
+ const utilityControlCount =
55
+ mobileUtilityItems.length + Number(resolvedHasUtilityActions);
54
56
  ---
55
57
 
56
58
  <div class="site-nav__mobile" data-mobile-navigation>
@@ -124,7 +126,10 @@ const resolvedHasUtilityActions =
124
126
  mobileFooterItems.length > 0 ? (
125
127
  <div class="site-nav__mobile-controls">
126
128
  {mobileUtilityItems.length > 0 || resolvedHasUtilityActions ? (
127
- <ul class="site-nav__mobile-utility">
129
+ <ul
130
+ class="site-nav__mobile-utility"
131
+ style={`--mobile-navigation-control-count: ${utilityControlCount};`}
132
+ >
128
133
  {mobileUtilityItems.map((item) => (
129
134
  <li>
130
135
  <NavigationLink
@@ -147,7 +152,10 @@ const resolvedHasUtilityActions =
147
152
  ) : null}
148
153
 
149
154
  {mobileFooterItems.length > 0 ? (
150
- <ul class="site-nav__mobile-footer">
155
+ <ul
156
+ class="site-nav__mobile-footer"
157
+ style={`--mobile-navigation-control-count: ${mobileFooterItems.length};`}
158
+ >
151
159
  {mobileFooterItems.map((item) => (
152
160
  <li>
153
161
  <NavigationLink
@@ -174,19 +174,14 @@
174
174
  .site-nav__mobile-utility,
175
175
  .site-nav__mobile-footer {
176
176
  display: grid;
177
+ grid-template-columns: repeat(
178
+ var(--mobile-navigation-control-count, 1),
179
+ minmax(0, 1fr)
180
+ );
177
181
  margin: 0;
178
182
  padding: 0;
179
- list-style: none;
180
- }
181
-
182
- .site-nav__mobile-utility {
183
- grid-template-columns: repeat(2, minmax(0, 1fr));
184
- gap: var(--nox-space-3);
185
- }
186
-
187
- .site-nav__mobile-footer {
188
- grid-template-columns: repeat(3, minmax(0, 1fr));
189
183
  gap: var(--nox-space-3);
184
+ list-style: none;
190
185
  }
191
186
 
192
187
  .site-nav__mobile-utility-link,
@@ -94,10 +94,30 @@ const jsonLdNodes = [
94
94
  <meta
95
95
  name="theme-color"
96
96
  content={themeColor.color}
97
+ data-theme-color={themeColor.theme}
97
98
  media={themeColor.media}
98
99
  />
99
100
  ))
100
101
  }
102
+ <script is:inline>
103
+ (() => {
104
+ const applyThemeColor = (theme) => {
105
+ if (theme !== 'light' && theme !== 'dark') return;
106
+
107
+ for (const meta of document.querySelectorAll(
108
+ 'meta[name="theme-color"][data-theme-color]',
109
+ )) {
110
+ if (!(meta instanceof HTMLMetaElement)) continue;
111
+ meta.media = meta.dataset.themeColor === theme ? 'all' : 'not all';
112
+ }
113
+ };
114
+
115
+ applyThemeColor(document.documentElement.dataset.theme);
116
+ document.addEventListener('astronox:theme-change', (event) => {
117
+ applyThemeColor(event.detail?.theme);
118
+ });
119
+ })();
120
+ </script>
101
121
  <meta name="application-name" content={site.identity.name} />
102
122
  <meta name="apple-mobile-web-app-title" content={site.identity.name} />
103
123
  {seo.description ? <meta name="description" content={seo.description} /> : null}
@@ -1,4 +1,5 @@
1
1
  import type { PageMeta } from './pageMeta';
2
+ import type { ColorTheme } from '../theme/contracts';
2
3
 
3
4
  export type SiteTextDirection = 'ltr' | 'rtl';
4
5
 
@@ -35,6 +36,7 @@ export type SiteIconConfig = {
35
36
  export type SiteThemeColor = {
36
37
  color: string;
37
38
  media?: string;
39
+ theme?: ColorTheme;
38
40
  };
39
41
 
40
42
  export type SiteSeoConfig = {
@@ -1,3 +1,10 @@
1
+ [data-theme-switching='true'],
2
+ [data-theme-switching='true'] *,
3
+ [data-theme-switching='true'] *::before,
4
+ [data-theme-switching='true'] *::after {
5
+ transition: none !important;
6
+ }
7
+
1
8
  [data-interaction-accent] {
2
9
  --nox-interaction-accent-hover-background: color-mix(
3
10
  in srgb,