@santi020k/lumen-astro 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.
Files changed (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/components/Accordion.astro +27 -0
  4. package/components/Affix.astro +41 -0
  5. package/components/Agenda.astro +19 -0
  6. package/components/Alert.astro +38 -0
  7. package/components/AlertDialog.astro +41 -0
  8. package/components/Anchor.astro +43 -0
  9. package/components/AnimatedLogo.astro +30 -0
  10. package/components/AnimatedNumber.astro +56 -0
  11. package/components/AnimatedPortrait.astro +124 -0
  12. package/components/AspectRatio.astro +39 -0
  13. package/components/Attachment.astro +46 -0
  14. package/components/Autocomplete.astro +24 -0
  15. package/components/Avatar.astro +36 -0
  16. package/components/BackToTop.astro +30 -0
  17. package/components/Badge.astro +36 -0
  18. package/components/Breadcrumb.astro +28 -0
  19. package/components/Bubble.astro +36 -0
  20. package/components/Button.astro +52 -0
  21. package/components/ButtonGroup.astro +27 -0
  22. package/components/ButtonLink.astro +58 -0
  23. package/components/Calendar.astro +197 -0
  24. package/components/Callout.astro +27 -0
  25. package/components/Card.astro +40 -0
  26. package/components/Carousel.astro +33 -0
  27. package/components/Cascader.astro +109 -0
  28. package/components/Chart.astro +32 -0
  29. package/components/Checkbox.astro +22 -0
  30. package/components/Code.astro +96 -0
  31. package/components/Collapsible.astro +28 -0
  32. package/components/ColorPicker.astro +16 -0
  33. package/components/Combobox.astro +67 -0
  34. package/components/Command.astro +33 -0
  35. package/components/ContextMenu.astro +44 -0
  36. package/components/CoverImage.astro +180 -0
  37. package/components/DataTable.astro +122 -0
  38. package/components/DatePicker.astro +108 -0
  39. package/components/DateRangePicker.astro +18 -0
  40. package/components/Descriptions.astro +41 -0
  41. package/components/Dialog.astro +40 -0
  42. package/components/Direction.astro +30 -0
  43. package/components/Drawer.astro +40 -0
  44. package/components/DropdownMenu.astro +40 -0
  45. package/components/Empty.astro +32 -0
  46. package/components/Eyebrow.astro +27 -0
  47. package/components/Field.astro +42 -0
  48. package/components/FileUpload.astro +44 -0
  49. package/components/FloatingBadge.astro +27 -0
  50. package/components/FormattedDate.astro +27 -0
  51. package/components/GradientDivider.astro +24 -0
  52. package/components/HoverCard.astro +40 -0
  53. package/components/Icon.astro +50 -0
  54. package/components/Image.astro +33 -0
  55. package/components/Input.astro +24 -0
  56. package/components/InputGroup.astro +27 -0
  57. package/components/InputOTP.astro +60 -0
  58. package/components/Item.astro +32 -0
  59. package/components/Kbd.astro +27 -0
  60. package/components/Label.astro +18 -0
  61. package/components/LanguageToggle.astro +30 -0
  62. package/components/Link.astro +27 -0
  63. package/components/Marker.astro +33 -0
  64. package/components/Mentions.astro +47 -0
  65. package/components/Menubar.astro +40 -0
  66. package/components/Message.astro +32 -0
  67. package/components/MessageScroller.astro +32 -0
  68. package/components/Meter.astro +31 -0
  69. package/components/NativeSelect.astro +37 -0
  70. package/components/NavigationMenu.astro +40 -0
  71. package/components/Note.astro +23 -0
  72. package/components/NumberField.astro +16 -0
  73. package/components/Pagination.astro +28 -0
  74. package/components/Particles.astro +31 -0
  75. package/components/PhoneInput.astro +86 -0
  76. package/components/Pill.astro +30 -0
  77. package/components/Popconfirm.astro +40 -0
  78. package/components/Popover.astro +40 -0
  79. package/components/Progress.astro +30 -0
  80. package/components/Prose.astro +27 -0
  81. package/components/QRCode.astro +44 -0
  82. package/components/RadioGroup.astro +28 -0
  83. package/components/Rating.astro +42 -0
  84. package/components/Resizable.astro +55 -0
  85. package/components/RevealGroup.astro +54 -0
  86. package/components/RichTextEditor.astro +19 -0
  87. package/components/Schedule.astro +19 -0
  88. package/components/ScrollArea.astro +32 -0
  89. package/components/ScrollReveal.astro +50 -0
  90. package/components/SearchField.astro +16 -0
  91. package/components/Segmented.astro +56 -0
  92. package/components/Select.astro +120 -0
  93. package/components/Separator.astro +31 -0
  94. package/components/Sheet.astro +40 -0
  95. package/components/Sidebar.astro +39 -0
  96. package/components/Skeleton.astro +36 -0
  97. package/components/SkipLink.astro +27 -0
  98. package/components/Slider.astro +20 -0
  99. package/components/Sonner.astro +38 -0
  100. package/components/SpeedDial.astro +77 -0
  101. package/components/Spinner.astro +36 -0
  102. package/components/Stat.astro +34 -0
  103. package/components/Stepper.astro +57 -0
  104. package/components/Switch.astro +23 -0
  105. package/components/Table.astro +32 -0
  106. package/components/Tabs.astro +33 -0
  107. package/components/TagGroup.astro +18 -0
  108. package/components/Textarea.astro +17 -0
  109. package/components/ThemeBuilder.astro +19 -0
  110. package/components/ThemeToggle.astro +41 -0
  111. package/components/TimeField.astro +16 -0
  112. package/components/Timeline.astro +12 -0
  113. package/components/TimelineItem.astro +19 -0
  114. package/components/Toast.astro +50 -0
  115. package/components/Toggle.astro +25 -0
  116. package/components/ToggleGroup.astro +28 -0
  117. package/components/Toolbar.astro +36 -0
  118. package/components/Tooltip.astro +27 -0
  119. package/components/Tour.astro +52 -0
  120. package/components/Transfer.astro +70 -0
  121. package/components/Tree.astro +19 -0
  122. package/components/TreeGrid.astro +19 -0
  123. package/components/TreeSelect.astro +69 -0
  124. package/components/Typography.astro +27 -0
  125. package/components/VirtualList.astro +19 -0
  126. package/components/Watermark.astro +60 -0
  127. package/index.d.ts +125 -0
  128. package/index.ts +125 -0
  129. package/package.json +74 -0
  130. package/runtime/UIPrimitives.astro +4649 -0
  131. package/styles/lumen.css +5 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Santiago Molina
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,142 @@
1
+ # @santi020k/lumen-astro
2
+
3
+ Production-ready Astro primitives for Lumen UI. The stylesheet is standalone CSS, so no Tailwind
4
+ configuration is required to render the components.
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ pnpm add @santi020k/lumen-astro
10
+ ```
11
+
12
+ Import the CSS once in your root layout and mount `UIPrimitives` once in that same app shell if you
13
+ use interactive components such as dialogs, menus, popovers, tabs, carousels, toggles, command
14
+ lists, or toasts. Do not add `UIPrimitives` beside each component instance; one root include
15
+ enhances all matching Lumen markup on the page.
16
+
17
+ ```css
18
+ @import "@santi020k/lumen-astro/styles.css";
19
+ ```
20
+
21
+ With Tailwind, keep the Lumen import in the same shared CSS entry:
22
+
23
+ ```css
24
+ @import "tailwindcss";
25
+ @import "@santi020k/lumen-astro/styles.css";
26
+ ```
27
+
28
+ ```astro
29
+ ---
30
+ import { UIPrimitives } from '@santi020k/lumen-astro'
31
+ ---
32
+
33
+ <UIPrimitives />
34
+ <slot />
35
+ ```
36
+
37
+ ```astro
38
+ ---
39
+ import { Button, Card, Input } from '@santi020k/lumen-astro'
40
+ ---
41
+
42
+ <Card>
43
+ <label for="email">Email</label>
44
+ <Input id="email" type="email" placeholder="you@example.com" />
45
+ <Button>Subscribe</Button>
46
+ </Card>
47
+ ```
48
+
49
+ Use `Icon` for Lucide icons by name across framework adapters.
50
+
51
+ ```astro
52
+ ---
53
+ import { Button, Icon } from '@santi020k/lumen-astro'
54
+ ---
55
+
56
+ <Button variant="secondary">
57
+ <Icon name="wand-sparkles" decorative />
58
+ Generate
59
+ </Button>
60
+ <Icon name="search" label="Search" />
61
+ ```
62
+
63
+ ## Optimized images
64
+
65
+ Lumen's Astro `Image` delegates to `astro:assets`, so imported image metadata, responsive layouts,
66
+ generated `srcset` values, output formats, quality, and remote-size inference stay available. It
67
+ loads lazily by default. Use `loading="eager"` and `fetchpriority="high"` only for the single image
68
+ that is likely to be the page's LCP image.
69
+
70
+ ```astro
71
+ ---
72
+ import { Image } from '@santi020k/lumen-astro'
73
+ import hero from '../assets/hero.jpg'
74
+ ---
75
+
76
+ <Image
77
+ alt="Team collaborating around a table"
78
+ layout="full-width"
79
+ quality="high"
80
+ src={hero}
81
+ />
82
+ ```
83
+
84
+ For monochrome artwork whose pixels were authored for a light background, add `invertOnDark`.
85
+ Colorful photography should keep the default so its colors remain unchanged.
86
+
87
+ `AnimatedLogo` accepts your own inline SVG rather than imposing brand artwork. Put the accessible
88
+ name on the SVG itself.
89
+
90
+ ```astro
91
+ ---
92
+ import { AnimatedLogo } from '@santi020k/lumen-astro'
93
+ ---
94
+
95
+ <AnimatedLogo style="height: 3rem">
96
+ <svg aria-label="Acme" role="img" viewBox="0 0 120 32">
97
+ <!-- Your logo artwork -->
98
+ </svg>
99
+ </AnimatedLogo>
100
+ ```
101
+
102
+ For a choreographed logo, set `animation="sequence"` and mark individual SVG layers with
103
+ `data-ui-logo-pop`, `data-ui-logo-draw`, or `data-ui-logo-reveal`. Timing can be adjusted per layer
104
+ with `--ui-logo-delay`.
105
+
106
+ The package ships the complete Astro primitive catalog plus a small progressive-enhancement runtime for dialogs, popovers, tabs, menus, command filtering, carousels, and toasts.
107
+
108
+ ## Motion
109
+
110
+ Use the shared `fast`, `standard`, and `slow` duration vocabulary across motion primitives.
111
+ `ScrollReveal` handles one entrance, `RevealGroup` staggers direct children, and `AnimatedNumber`
112
+ animates a formatted metric while preserving a stable accessible value. All three keep their final
113
+ content readable without JavaScript and honor reduced-motion preferences.
114
+
115
+ ```astro
116
+ <ScrollReveal animation="slide-up" duration="slow">
117
+ <h2>Release highlights</h2>
118
+ </ScrollReveal>
119
+
120
+ <RevealGroup stagger={80}>
121
+ <Card>Plan</Card>
122
+ <Card>Build</Card>
123
+ <Card>Ship</Card>
124
+ </RevealGroup>
125
+
126
+ <AnimatedNumber decimals={1} suffix="%" value={99.8} />
127
+ ```
128
+
129
+ ## Glass surfaces
130
+
131
+ Lumen includes glassmorphism tokens and reusable classes in the shared stylesheet. Cards use a
132
+ boolean prop, and overlays or navigation primitives use the same prop while preserving semantic
133
+ variants. The older `variant="glass"` form remains supported for cards. Overlay
134
+ `surface="glass"` props are deprecated compatibility aliases that map to `glass`.
135
+
136
+ ```astro
137
+ <Card glass>Glass card</Card>
138
+ <Dialog glass>Glass dialog</Dialog>
139
+ <Popover glass>Glass popover</Popover>
140
+ <DatePicker glass="subtle" />
141
+ <Select glass="strong" options={['Astro', 'React']} />
142
+ ```
@@ -0,0 +1,27 @@
1
+ ---
2
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
+
4
+ import type { HTMLAttributes } from 'astro/types'
5
+
6
+ interface Props extends HTMLAttributes<'div'> {
7
+ class?: string
8
+ className?: string
9
+ }
10
+
11
+ const {
12
+ class: classProp = '',
13
+ className = '',
14
+ ...rest
15
+ } = Astro.props
16
+
17
+ const { classList, passthrough } = resolveLumenAstroProps(rest, [
18
+ 'ui-accordion'
19
+ ], classProp, className)
20
+ ---
21
+
22
+ <div
23
+ class:list={classList}
24
+ {...passthrough}
25
+ >
26
+ <slot />
27
+ </div>
@@ -0,0 +1,41 @@
1
+ ---
2
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
+
4
+ import type { HTMLAttributes } from 'astro/types'
5
+
6
+ interface Props extends HTMLAttributes<'div'> {
7
+ class?: string
8
+ className?: string
9
+ offset?: number
10
+ offsetBottom?: number
11
+ offsetTop?: number
12
+ position?: 'top' | 'bottom'
13
+ }
14
+
15
+ const {
16
+ class: classProp = '',
17
+ className = '',
18
+ offset = 0,
19
+ offsetBottom,
20
+ offsetTop,
21
+ position: positionProp,
22
+ ...rest
23
+ } = Astro.props
24
+
25
+ const position = positionProp ?? (offsetBottom !== undefined ? 'bottom' : 'top')
26
+ const resolvedOffset = offsetBottom ?? offsetTop ?? offset
27
+
28
+ const { classList, passthrough } = resolveLumenAstroProps(rest, [
29
+ 'ui-affix',
30
+ position === 'bottom' && 'ui-affix--bottom'
31
+ ], classProp, className)
32
+ ---
33
+
34
+ <div
35
+ class:list={classList}
36
+ data-position={position}
37
+ style={`--ui-affix-offset:${resolvedOffset}px`}
38
+ {...passthrough}
39
+ >
40
+ <slot />
41
+ </div>
@@ -0,0 +1,19 @@
1
+ ---
2
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
+
4
+ import type { HTMLAttributes } from 'astro/types'
5
+
6
+ interface Props extends HTMLAttributes<'section'> {
7
+ class?: string
8
+ className?: string
9
+ glass?: boolean | 'subtle' | 'strong'
10
+ }
11
+
12
+ const { class: classProp = '', className = '', glass = false, ...rest } = Astro.props
13
+ const { classList, passthrough } = resolveLumenAstroProps(rest, ['ui-agenda', glass && 'ui-agenda--glass', glass === 'subtle' && 'ui-glass-subtle', glass === 'strong' && 'ui-glass-strong'
14
+ ], classProp, className)
15
+ ---
16
+
17
+ <section class:list={classList} {...passthrough}>
18
+ <slot />
19
+ </section>
@@ -0,0 +1,38 @@
1
+ ---
2
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
+
4
+ import type { HTMLAttributes } from 'astro/types'
5
+
6
+ interface Props extends HTMLAttributes<'aside'> {
7
+ class?: string
8
+ className?: string
9
+ glass?: boolean | 'subtle' | 'strong'
10
+ variant?: 'default' | 'destructive' | 'success' | 'warning'
11
+ }
12
+
13
+ const {
14
+ class: classProp = '',
15
+ className = '',
16
+ glass = false,
17
+ variant = 'default',
18
+ ...rest
19
+ } = Astro.props
20
+
21
+ const { classList, passthrough } = resolveLumenAstroProps(rest, [
22
+ 'ui-alert',
23
+ variant === 'destructive' && 'ui-alert--destructive',
24
+ variant === 'success' && 'ui-alert--success',
25
+ variant === 'warning' && 'ui-alert--warning',
26
+ glass && 'ui-alert--glass',
27
+ glass === 'subtle' && 'ui-glass-subtle',
28
+ glass === 'strong' && 'ui-glass-strong'
29
+ ], classProp, className)
30
+ ---
31
+
32
+ <aside
33
+ data-variant={variant}
34
+ class:list={classList}
35
+ {...passthrough}
36
+ >
37
+ <slot />
38
+ </aside>
@@ -0,0 +1,41 @@
1
+ ---
2
+ import { resolveLumenAstroProps, resolveLumenGlass } from '@santi020k/lumen-core'
3
+
4
+ import type { HTMLAttributes } from 'astro/types'
5
+
6
+ interface Props extends HTMLAttributes<'dialog'> {
7
+ class?: string
8
+ className?: string
9
+ glass?: boolean | 'subtle' | 'strong'
10
+ /**
11
+ * @deprecated Use `glass` instead. `surface="glass"` maps to `glass={true}`.
12
+ */
13
+ surface?: 'default' | 'glass'
14
+ }
15
+
16
+ const {
17
+ class: classProp = '',
18
+ className = '',
19
+ glass = false,
20
+ surface = 'default',
21
+ ...rest
22
+ } = Astro.props
23
+
24
+ const resolvedGlass = resolveLumenGlass(glass, surface)
25
+ const { classList, passthrough } = resolveLumenAstroProps(rest, [
26
+ 'ui-dialog ui-alert-dialog',
27
+ resolvedGlass && 'ui-dialog--glass',
28
+ glass === 'subtle' && 'ui-glass-subtle',
29
+ glass === 'strong' && 'ui-glass-strong'
30
+ ], classProp, className)
31
+ ---
32
+
33
+ <dialog
34
+ data-surface={resolvedGlass ? 'glass' : 'default'}
35
+ data-ui-alert-dialog
36
+ role="alertdialog"
37
+ class:list={classList}
38
+ {...passthrough}
39
+ >
40
+ <slot />
41
+ </dialog>
@@ -0,0 +1,43 @@
1
+ ---
2
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
+
4
+ import type { HTMLAttributes } from 'astro/types'
5
+
6
+ type AnchorLink = { href: string; label: string }
7
+
8
+ export interface Props extends HTMLAttributes<'nav'> {
9
+ class?: string
10
+ className?: string
11
+ items?: AnchorLink[]
12
+ }
13
+
14
+ const {
15
+ 'aria-label': ariaLabel = 'On this page',
16
+ class: classProp = '',
17
+ className = '',
18
+ items = [],
19
+ ...rest
20
+ } = Astro.props
21
+
22
+ const { classList, passthrough } = resolveLumenAstroProps(rest, [
23
+ 'ui-anchor'
24
+ ], classProp, className)
25
+ ---
26
+
27
+ <nav
28
+ aria-label={ariaLabel}
29
+ class:list={classList}
30
+ data-ui-anchor
31
+ {...passthrough}
32
+ >
33
+ {items.length > 0 && (
34
+ <ol>
35
+ {items.map((item, index) => (
36
+ <li>
37
+ <a data-active={index === 0 ? 'true' : 'false'} href={item.href}>{item.label}</a>
38
+ </li>
39
+ ))}
40
+ </ol>
41
+ )}
42
+ <slot />
43
+ </nav>
@@ -0,0 +1,30 @@
1
+ ---
2
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
+ import type { HTMLAttributes } from 'astro/types'
4
+
5
+ interface Props extends HTMLAttributes<'span'> {
6
+ animation?: 'reveal' | 'sequence'
7
+ class?: string
8
+ className?: string
9
+ }
10
+
11
+ const {
12
+ animation = 'reveal',
13
+ class: classProp = '',
14
+ className = '',
15
+ ...rest
16
+ } = Astro.props
17
+
18
+ const { classList, passthrough } = resolveLumenAstroProps(rest, [
19
+ 'ui-animated-logo'
20
+ ], classProp, className)
21
+ ---
22
+
23
+ <span
24
+ class:list={classList}
25
+ data-animation={animation}
26
+ data-ui-animated-logo
27
+ {...passthrough}
28
+ >
29
+ <slot />
30
+ </span>
@@ -0,0 +1,56 @@
1
+ ---
2
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
+
4
+ import type { HTMLAttributes } from 'astro/types'
5
+
6
+ interface Props extends Omit<HTMLAttributes<'span'>, 'prefix'> {
7
+ class?: string
8
+ className?: string
9
+ decimals?: number
10
+ duration?: 'fast' | 'standard' | 'slow'
11
+ from?: number
12
+ locale?: string
13
+ prefix?: string
14
+ suffix?: string
15
+ value: number
16
+ }
17
+
18
+ const {
19
+ class: classProp = '',
20
+ className = '',
21
+ decimals = 0,
22
+ duration = 'slow',
23
+ from = 0,
24
+ locale,
25
+ prefix = '',
26
+ suffix = '',
27
+ value,
28
+ ...rest
29
+ } = Astro.props
30
+
31
+ const safeDecimals = Math.max(0, Math.min(20, Math.floor(decimals)))
32
+ const formatter = new Intl.NumberFormat(locale, {
33
+ maximumFractionDigits: safeDecimals,
34
+ minimumFractionDigits: safeDecimals
35
+ })
36
+ const formattedValue = `${prefix}${formatter.format(value)}${suffix}`
37
+ const { classList, passthrough } = resolveLumenAstroProps(rest, [
38
+ 'ui-animated-number',
39
+ `ui-motion-duration-${duration}`
40
+ ], classProp, className)
41
+ ---
42
+
43
+ <span
44
+ class:list={classList}
45
+ data-ui-animated-number
46
+ data-ui-animated-number-decimals={safeDecimals}
47
+ data-ui-animated-number-from={from}
48
+ data-ui-animated-number-locale={locale}
49
+ data-ui-animated-number-prefix={prefix}
50
+ data-ui-animated-number-suffix={suffix}
51
+ data-ui-animated-number-value={value}
52
+ {...passthrough}
53
+ >
54
+ <span aria-hidden="true" data-ui-animated-number-output>{formattedValue}</span>
55
+ <span class="ui-sr-only">{formattedValue}</span>
56
+ </span>
@@ -0,0 +1,124 @@
1
+ ---
2
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
+ import type { ImageMetadata } from 'astro'
4
+ import { Picture } from 'astro:assets'
5
+
6
+ import type { HTMLAttributes } from 'astro/types'
7
+
8
+ interface Props extends HTMLAttributes<'div'> {
9
+ alt: string
10
+ badgeProps?: {
11
+ experience?: {
12
+ label: string
13
+ value: string
14
+ }
15
+ location?: {
16
+ label: string
17
+ text: string
18
+ }
19
+ }
20
+ class?: string
21
+ className?: string
22
+ showFloatingBadges?: boolean
23
+ sizes?: string
24
+ src: ImageMetadata | string
25
+ widths?: number[]
26
+ }
27
+
28
+ const {
29
+ alt,
30
+ badgeProps,
31
+ class: classProp = '',
32
+ className = '',
33
+ showFloatingBadges = false,
34
+ sizes = '(min-width: 1024px) 352px, 75vw',
35
+ src,
36
+ widths = [400, 600, 800],
37
+ ...rest
38
+ } = Astro.props
39
+
40
+ const { classList, passthrough } = resolveLumenAstroProps(rest, [
41
+ 'ui-animated-portrait',
42
+ 'relative isolate mx-auto w-full max-w-88 animate-reveal-lcp motion-reduce:animate-none'
43
+ ], classProp, className)
44
+ ---
45
+
46
+ <div class:list={classList} {...passthrough}>
47
+ <div
48
+ aria-hidden="true"
49
+ class="ui-animated-portrait__glow"
50
+ />
51
+
52
+ <div
53
+ aria-hidden="true"
54
+ class="ui-animated-portrait__orbit ui-animated-portrait__orbit--outer"
55
+ />
56
+
57
+ <div
58
+ aria-hidden="true"
59
+ class="ui-animated-portrait__orbit ui-animated-portrait__orbit--inner"
60
+ />
61
+
62
+ <div
63
+ class="panel-card ui-animated-portrait__card"
64
+ >
65
+ <div
66
+ aria-hidden="true"
67
+ class="grid-fade ui-animated-portrait__grid"
68
+ />
69
+ <div
70
+ aria-hidden="true"
71
+ class="ui-animated-portrait__wash"
72
+ />
73
+ {typeof src === 'string'
74
+ ? (
75
+ <Picture
76
+ alt={alt}
77
+ class="ui-animated-portrait__image"
78
+ decoding="sync"
79
+ fetchpriority="high"
80
+ formats={['avif', 'webp']}
81
+ height={440}
82
+ loading="eager"
83
+ sizes={sizes}
84
+ src={src}
85
+ width={352}
86
+ widths={widths}
87
+ />
88
+ )
89
+ : (
90
+ <Picture
91
+ alt={alt}
92
+ class="ui-animated-portrait__image"
93
+ decoding="sync"
94
+ fetchpriority="high"
95
+ formats={['avif', 'webp']}
96
+ height={440}
97
+ loading="eager"
98
+ sizes={sizes}
99
+ src={src}
100
+ width={352}
101
+ widths={widths}
102
+ />
103
+ )}
104
+ </div>
105
+
106
+ {showFloatingBadges && badgeProps?.location && (
107
+ <div class="ui-animated-portrait__badge ui-animated-portrait__badge--location">
108
+ <p class="ui-animated-portrait__label ui-animated-portrait__label--location">
109
+ <span class="ui-animated-portrait__status" />
110
+ {badgeProps.location.label}
111
+ </p>
112
+ <p class="ui-animated-portrait__location">{badgeProps.location.text}</p>
113
+ </div>
114
+ )}
115
+
116
+ {showFloatingBadges && badgeProps?.experience && (
117
+ <div class="ui-animated-portrait__badge ui-animated-portrait__badge--experience">
118
+ <p class="ui-animated-portrait__label">{badgeProps.experience.label}</p>
119
+ <p class="ui-animated-portrait__experience">
120
+ {badgeProps.experience.value}
121
+ </p>
122
+ </div>
123
+ )}
124
+ </div>
@@ -0,0 +1,39 @@
1
+ ---
2
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
+
4
+ import type { HTMLAttributes } from 'astro/types'
5
+
6
+ interface Props extends HTMLAttributes<'div'> {
7
+ class?: string
8
+ className?: string
9
+ ratio?: number | string
10
+ }
11
+
12
+ const {
13
+ class: classProp = '',
14
+ className = '',
15
+ ratio = '16 / 9',
16
+ ...rest
17
+ } = Astro.props
18
+
19
+ const { classList, passthrough } = resolveLumenAstroProps(rest, [
20
+ 'ui-aspect-ratio'
21
+ ], classProp, className)
22
+ const ratioValue = typeof ratio === 'number' && Number.isFinite(ratio)
23
+ ? String(ratio)
24
+ : typeof ratio === 'string'
25
+ ? ratio.trim()
26
+ : ''
27
+ const isValidRatio =
28
+ /^\d+(\.\d+)?(\s*\/\s*\d+(\.\d+)?)?$/.test(ratioValue) &&
29
+ ratioValue.split('/').every(value => Number(value.trim()) > 0)
30
+ const safeRatio = isValidRatio ? ratioValue : '16 / 9'
31
+ ---
32
+
33
+ <div
34
+ style={`aspect-ratio: ${safeRatio}`}
35
+ class:list={classList}
36
+ {...passthrough}
37
+ >
38
+ <slot />
39
+ </div>
@@ -0,0 +1,46 @@
1
+ ---
2
+ import { resolveLumenAstroProps } from '@santi020k/lumen-core'
3
+
4
+ import type { HTMLAttributes } from 'astro/types'
5
+
6
+ interface Props extends HTMLAttributes<'article'> {
7
+ class?: string
8
+ className?: string
9
+ glass?: boolean | 'subtle' | 'strong'
10
+ href?: string
11
+ }
12
+
13
+ const {
14
+ class: classProp = '',
15
+ className = '',
16
+ glass = false,
17
+ href,
18
+ ...rest
19
+ } = Astro.props
20
+
21
+ const { classList, passthrough } = resolveLumenAstroProps(rest, [
22
+ 'ui-attachment',
23
+ glass && 'ui-attachment--glass',
24
+ glass === 'subtle' && 'ui-glass-subtle',
25
+ glass === 'strong' && 'ui-glass-strong'
26
+ ], classProp, className)
27
+ ---
28
+
29
+ {href
30
+ ? (
31
+ <a
32
+ href={href}
33
+ class:list={classList}
34
+ {...passthrough}
35
+ >
36
+ <slot />
37
+ </a>
38
+ )
39
+ : (
40
+ <article
41
+ class:list={classList}
42
+ {...passthrough}
43
+ >
44
+ <slot />
45
+ </article>
46
+ )}