@sarimarcus/content-sites-ui 0.30.1 → 0.31.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 CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All packages in the platform release in lockstep; entries are per release version.
4
4
 
5
+ ## 0.31.0 (2026-10-08)
6
+
7
+ - `./editorial` adds `AlsoExplore` (`AlsoExploreItem`) and `VisitTiming` (`VisitTimingData`; props `title`, `id`, `class` default `mt-12`, `labelClass` default `text-[0.8125rem]`), moved from the sites (SLA-3017).
8
+
9
+ ### Breaking
10
+
11
+ - The sites' `components/ui/AlsoExplore.astro` and `VisitTiming.astro` now live in ui; sites run `rewrite-imports.mjs --delete-local`, then `scripts/platform/migrations/core-markdown.mjs` and `scripts/platform/migrations/promoted-site-files.mjs` (Dark Skies' two `<VisitTiming>` calls get `labelClass="text-sm"`) (SLA-3017).
12
+
13
+
5
14
  ## 0.30.1 (2026-10-07)
6
15
 
7
16
  - `./editorial`: `StaticProsePage` takes `proseClass`, the variant beside `prose` (default `prose-stone`) and `NotFoundPage` takes `searchBoxClass` (default: the current search-link classes), so a dark theme can pass its own prose variant and input tokens; default output is unchanged (SLA-2989).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarimarcus/content-sites-ui",
3
- "version": "0.30.1",
3
+ "version": "0.31.0",
4
4
  "description": "Universal Astro UI components, layout shell and design tokens shared by the content sites.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -49,6 +49,6 @@
49
49
  "@lucide/astro": "^1.23.0",
50
50
  "astro": "^7.0.6",
51
51
  "tailwindcss": "^4.1.18",
52
- "@sarimarcus/content-sites-core": "0.30.1"
52
+ "@sarimarcus/content-sites-core": "0.31.0"
53
53
  }
54
54
  }
@@ -0,0 +1,37 @@
1
+ ---
2
+ /** "Also explore": up to four sibling links under a hairline, at the foot of a hub or listing page. */
3
+ import Icon from '../primitives/Icon.astro';
4
+
5
+ export interface AlsoExploreItem {
6
+ href: string;
7
+ label: string;
8
+ }
9
+
10
+ interface Props {
11
+ eyebrow?: string;
12
+ items: AlsoExploreItem[];
13
+ class?: string;
14
+ }
15
+
16
+ const { eyebrow = 'Also explore', items, class: className } = Astro.props;
17
+
18
+ if (import.meta.env.DEV && items.length > 4) {
19
+ console.warn(`AlsoExplore: received ${items.length} items, capping at 4.`);
20
+ }
21
+
22
+ const displayItems = items.slice(0, 4);
23
+ ---
24
+
25
+ {displayItems.length > 0 && (
26
+ <nav class:list={['mt-12 pt-8 border-t border-border', className]} aria-label={eyebrow}>
27
+ <p class="text-xs font-semibold uppercase tracking-widest text-muted-foreground mb-3">{eyebrow}</p>
28
+ <div class="flex flex-wrap gap-x-6 gap-y-2">
29
+ {displayItems.map((item) => (
30
+ <a href={item.href} class="inline-flex items-center gap-1.5 text-sm text-foreground hover:text-primary transition-colors">
31
+ {item.label}
32
+ <Icon name="arrow-right" class="w-3 h-3" />
33
+ </a>
34
+ ))}
35
+ </div>
36
+ </nav>
37
+ )}
@@ -0,0 +1,73 @@
1
+ ---
2
+ /**
3
+ * "When to Visit": best season, quietest hours and visit duration, each shown only when set. `class` replaces the
4
+ * default top margin (`mt-12`); `labelClass` sets the slot label size.
5
+ */
6
+ import Icon from '../primitives/Icon.astro';
7
+
8
+ export interface VisitTimingData {
9
+ bestSeason?: string;
10
+ quietestHours?: string;
11
+ visitDuration?: string;
12
+ }
13
+
14
+ interface Props {
15
+ visitTiming?: VisitTimingData;
16
+ /** Heading text — override to avoid colliding with nearby "When to…" headings. */
17
+ title?: string;
18
+ /** Section id — override when a prose section on the page could slugify to the same value. */
19
+ id?: string;
20
+ class?: string;
21
+ labelClass?: string;
22
+ }
23
+
24
+ const {
25
+ visitTiming,
26
+ title = 'When to Visit',
27
+ id = 'when-to-visit',
28
+ class: className = 'mt-12',
29
+ labelClass = 'text-[0.8125rem]',
30
+ } = Astro.props;
31
+
32
+ const slots = visitTiming
33
+ ? [
34
+ visitTiming.bestSeason
35
+ ? { icon: 'calendar', label: 'Best season', value: visitTiming.bestSeason }
36
+ : null,
37
+ visitTiming.quietestHours
38
+ ? { icon: 'clock', label: 'Quietest hours', value: visitTiming.quietestHours }
39
+ : null,
40
+ visitTiming.visitDuration
41
+ ? { icon: 'hourglass', label: 'Visit duration', value: visitTiming.visitDuration }
42
+ : null,
43
+ ].filter((s): s is { icon: string; label: string; value: string } => s !== null)
44
+ : [];
45
+ ---
46
+
47
+ {slots.length > 0 && (
48
+ <section
49
+ id={id}
50
+ class:list={['scroll-mt-28', className]}
51
+ data-guide-section
52
+ >
53
+ <h2 class="text-2xl font-display font-semibold text-foreground mb-6 leading-tight">{title}</h2>
54
+ <div
55
+ class:list={[
56
+ 'bg-primary/5 border border-primary/15 rounded-xl p-6 grid gap-6',
57
+ slots.length === 1 && 'grid-cols-1',
58
+ slots.length === 2 && 'grid-cols-1 sm:grid-cols-2',
59
+ slots.length === 3 && 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3',
60
+ ]}
61
+ >
62
+ {slots.map((slot) => (
63
+ <div class="min-w-0">
64
+ <p class:list={['flex items-center gap-1.5', labelClass, 'uppercase tracking-wider text-muted-foreground mb-1.5 leading-none']}>
65
+ <Icon name={slot.icon} class="w-3.5 h-3.5 text-primary/60 shrink-0" />
66
+ {slot.label}
67
+ </p>
68
+ <p class="text-base font-medium text-foreground leading-snug tabular-nums">{slot.value}</p>
69
+ </div>
70
+ ))}
71
+ </div>
72
+ </section>
73
+ )}
@@ -20,6 +20,10 @@ export type { AuthorSummary, ArticleSummary, KeyFact, SourceItem, ArticleCategor
20
20
  export { default as InsiderTips } from './InsiderTips.astro';
21
21
  export type { InsiderTip, InsiderTipAuthor, InsiderTipKinds, InsiderTipTone } from './types';
22
22
  export { default as AuthorStrip } from './AuthorStrip.astro';
23
+ export { default as AlsoExplore } from './AlsoExplore.astro';
24
+ export { default as VisitTiming } from './VisitTiming.astro';
25
+ export type { AlsoExploreItem } from './AlsoExplore.astro';
26
+ export type { VisitTimingData } from './VisitTiming.astro';
23
27
  export { default as AnswerBox } from './interludes/AnswerBox.astro';
24
28
  export { default as ComparisonTable } from './interludes/ComparisonTable.astro';
25
29
  export { default as EvidenceContext } from './interludes/EvidenceContext.astro';