@refraction-ui/astro 0.18.0 → 0.19.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/dist/action-list/action-list.styles.ts +33 -0
- package/dist/action-list/action-list.ts +31 -0
- package/dist/action-list/index.ts +16 -0
- package/dist/astro-action-list/ActionList.astro +12 -0
- package/dist/astro-action-list/ActionListItem.astro +62 -0
- package/dist/astro-action-list/index.ts +1 -0
- package/dist/astro-callout/Callout.astro +4 -1
- package/dist/astro-card/Card.astro +3 -2
- package/dist/astro-card/CardTitle.astro +6 -4
- package/dist/astro-funnel-chart/FunnelChart.astro +64 -0
- package/dist/astro-funnel-chart/index.ts +1 -0
- package/dist/astro-line-chart/LineChart.astro +82 -0
- package/dist/astro-line-chart/index.ts +1 -0
- package/dist/astro-page-header/PageHeader.astro +46 -0
- package/dist/astro-page-header/index.ts +1 -0
- package/dist/avatar-group/avatar-group.styles.ts +1 -1
- package/dist/badge/badge.styles.ts +6 -1
- package/dist/badge/badge.ts +24 -2
- package/dist/button/button.styles.ts +10 -4
- package/dist/button/button.ts +4 -1
- package/dist/callout/callout.ts +12 -3
- package/dist/card/card.styles.ts +10 -1
- package/dist/card/card.ts +14 -0
- package/dist/card/index.ts +3 -0
- package/dist/diff-viewer/diff-viewer.styles.ts +3 -0
- package/dist/funnel-chart/funnel-chart.styles.ts +16 -0
- package/dist/funnel-chart/funnel-chart.ts +68 -0
- package/dist/funnel-chart/index.ts +10 -0
- package/dist/index.ts +4 -0
- package/dist/input/input.styles.ts +2 -2
- package/dist/line-chart/index.ts +19 -0
- package/dist/line-chart/line-chart.styles.ts +16 -0
- package/dist/line-chart/line-chart.ts +188 -0
- package/dist/page-header/index.ts +15 -0
- package/dist/page-header/page-header.styles.ts +21 -0
- package/dist/page-header/page-header.ts +27 -0
- package/dist/presence-indicator/presence-indicator.styles.ts +1 -1
- package/dist/progress-display/progress-display.styles.ts +1 -1
- package/dist/select/index.ts +2 -0
- package/dist/select/select.styles.ts +9 -3
- package/dist/select/select.ts +11 -3
- package/dist/separator/index.ts +2 -0
- package/dist/separator/separator.styles.ts +17 -2
- package/dist/shared/cn.ts +6 -3
- package/dist/slide-viewer/slide-viewer.styles.ts +2 -0
- package/dist/status-indicator/status-indicator.styles.ts +4 -4
- package/dist/sticky-note/sticky-note.styles.ts +2 -0
- package/dist/tabs/index.ts +1 -0
- package/dist/tabs/tabs.ts +29 -0
- package/dist/textarea/textarea.styles.ts +2 -2
- package/package.json +5 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { cva } from '../shared/index.ts'
|
|
2
|
+
|
|
3
|
+
/** The list: rows separated by hairlines, no bullets. */
|
|
4
|
+
export const actionListClass = 'm-0 flex list-none flex-col divide-y divide-border-subtle p-0'
|
|
5
|
+
|
|
6
|
+
/** A row button: full width, whole row is the hit target. */
|
|
7
|
+
export const actionListItemVariants = cva({
|
|
8
|
+
base:
|
|
9
|
+
'flex w-full items-center gap-4 bg-transparent text-left text-sm text-foreground transition-colors ' +
|
|
10
|
+
'hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring ' +
|
|
11
|
+
'disabled:pointer-events-none disabled:opacity-50',
|
|
12
|
+
variants: {
|
|
13
|
+
density: {
|
|
14
|
+
default: 'px-4 py-3',
|
|
15
|
+
compact: 'px-3 py-2',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
density: 'default',
|
|
20
|
+
},
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
/** Leading text column (title, description, meta). */
|
|
24
|
+
export const actionListItemBodyClass = 'flex min-w-0 flex-1 flex-col gap-0.5'
|
|
25
|
+
/** Primary line. */
|
|
26
|
+
export const actionListItemTitleClass = 'break-words font-semibold text-foreground'
|
|
27
|
+
/** Supporting line under the title. */
|
|
28
|
+
export const actionListItemDescriptionClass = 'break-words text-xs text-muted-foreground'
|
|
29
|
+
/** Tertiary line (owner, tags, ids). */
|
|
30
|
+
export const actionListItemMetaClass = 'break-words text-xs text-muted-foreground'
|
|
31
|
+
/** Trailing column (dates, sizes, counts). */
|
|
32
|
+
export const actionListItemTrailingClass =
|
|
33
|
+
'flex flex-none items-center gap-4 text-xs tabular-nums text-muted-foreground'
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Row padding. */
|
|
2
|
+
export type ActionListDensity = 'default' | 'compact'
|
|
3
|
+
|
|
4
|
+
export interface ActionListItemProps {
|
|
5
|
+
disabled?: boolean
|
|
6
|
+
density?: ActionListDensity
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ActionListItemAPI {
|
|
10
|
+
/** ARIA attributes for the row button. */
|
|
11
|
+
ariaProps: Record<string, string | number | boolean>
|
|
12
|
+
/** Data attributes for styling hooks. */
|
|
13
|
+
dataAttributes: Record<string, string>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Build the framework-agnostic props for one ActionList row. Each row is ONE
|
|
18
|
+
* action (open, select, go to) rendered as a native button, so it is reachable
|
|
19
|
+
* by Tab and activated by Enter / Space — unlike a clickable table row.
|
|
20
|
+
*/
|
|
21
|
+
export function createActionListItem(props: ActionListItemProps = {}): ActionListItemAPI {
|
|
22
|
+
const { disabled = false, density = 'default' } = props
|
|
23
|
+
const dataAttributes: Record<string, string> = {
|
|
24
|
+
'data-slot': 'action-list-item',
|
|
25
|
+
'data-density': density,
|
|
26
|
+
}
|
|
27
|
+
if (disabled) dataAttributes['data-disabled'] = ''
|
|
28
|
+
const ariaProps: Record<string, string | number | boolean> = {}
|
|
29
|
+
if (disabled) ariaProps['aria-disabled'] = true
|
|
30
|
+
return { ariaProps, dataAttributes }
|
|
31
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createActionListItem,
|
|
3
|
+
type ActionListDensity,
|
|
4
|
+
type ActionListItemProps,
|
|
5
|
+
type ActionListItemAPI,
|
|
6
|
+
} from './action-list.js'
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
actionListClass,
|
|
10
|
+
actionListItemVariants,
|
|
11
|
+
actionListItemBodyClass,
|
|
12
|
+
actionListItemTitleClass,
|
|
13
|
+
actionListItemDescriptionClass,
|
|
14
|
+
actionListItemMetaClass,
|
|
15
|
+
actionListItemTrailingClass,
|
|
16
|
+
} from './action-list.styles.js'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { actionListClass } from '../action-list/index.ts'
|
|
3
|
+
import { cn } from '../shared/index.ts'
|
|
4
|
+
|
|
5
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {}
|
|
6
|
+
|
|
7
|
+
const { class: className, ...rest } = Astro.props
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
<ul class={cn(actionListClass, className)} data-slot="action-list" {...rest}>
|
|
11
|
+
<slot />
|
|
12
|
+
</ul>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
createActionListItem,
|
|
4
|
+
actionListItemVariants,
|
|
5
|
+
actionListItemBodyClass,
|
|
6
|
+
actionListItemTitleClass,
|
|
7
|
+
actionListItemDescriptionClass,
|
|
8
|
+
actionListItemMetaClass,
|
|
9
|
+
actionListItemTrailingClass,
|
|
10
|
+
type ActionListDensity,
|
|
11
|
+
} from '../action-list/index.ts'
|
|
12
|
+
import { cn } from '../shared/index.ts'
|
|
13
|
+
|
|
14
|
+
interface Props extends astroHTML.JSX.ButtonHTMLAttributes {
|
|
15
|
+
/** Primary line. */
|
|
16
|
+
title: string
|
|
17
|
+
/** Optional supporting line under the title. */
|
|
18
|
+
description?: string
|
|
19
|
+
/** Optional tertiary line. */
|
|
20
|
+
meta?: string
|
|
21
|
+
/** Optional trailing column (or use the `trailing` slot). */
|
|
22
|
+
trailing?: string
|
|
23
|
+
/** Row padding. */
|
|
24
|
+
density?: ActionListDensity
|
|
25
|
+
disabled?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const {
|
|
29
|
+
title,
|
|
30
|
+
description,
|
|
31
|
+
meta,
|
|
32
|
+
trailing,
|
|
33
|
+
density = 'default',
|
|
34
|
+
disabled = false,
|
|
35
|
+
class: className,
|
|
36
|
+
...rest
|
|
37
|
+
} = Astro.props
|
|
38
|
+
const api = createActionListItem({ disabled, density })
|
|
39
|
+
const hasTrailing = trailing != null || Astro.slots.has('trailing')
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
<li>
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
disabled={disabled}
|
|
46
|
+
class={cn(actionListItemVariants({ density }), className)}
|
|
47
|
+
{...api.ariaProps}
|
|
48
|
+
{...api.dataAttributes}
|
|
49
|
+
{...rest}
|
|
50
|
+
>
|
|
51
|
+
<span class={actionListItemBodyClass}>
|
|
52
|
+
<span class={actionListItemTitleClass}>{title}</span>
|
|
53
|
+
{description && <span class={actionListItemDescriptionClass}>{description}</span>}
|
|
54
|
+
{meta && <span class={actionListItemMetaClass}>{meta}</span>}
|
|
55
|
+
</span>
|
|
56
|
+
{hasTrailing && (
|
|
57
|
+
<span class={actionListItemTrailingClass}>
|
|
58
|
+
<slot name="trailing">{trailing}</slot>
|
|
59
|
+
</span>
|
|
60
|
+
)}
|
|
61
|
+
</button>
|
|
62
|
+
</li>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -7,7 +7,10 @@ interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
const { variant = 'default', class: className, ...props } = Astro.props
|
|
10
|
-
const api = createCallout({
|
|
10
|
+
const api = createCallout({
|
|
11
|
+
destructive: variant === 'destructive',
|
|
12
|
+
labelled: Boolean(props['aria-label'] || props['aria-labelledby']),
|
|
13
|
+
})
|
|
11
14
|
---
|
|
12
15
|
|
|
13
16
|
<div
|
|
@@ -3,15 +3,16 @@ import { createCard, cardVariants } from '../card/index.ts'
|
|
|
3
3
|
import { cn } from '../shared/index.ts'
|
|
4
4
|
|
|
5
5
|
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
6
|
+
variant?: 'default' | 'subtle' | 'tertiary'
|
|
6
7
|
padding?: 'none' | 'default' | 'compact'
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
const { padding, class: className, ...props } = Astro.props
|
|
10
|
+
const { variant, padding, class: className, ...props } = Astro.props
|
|
10
11
|
const api = createCard(props)
|
|
11
12
|
---
|
|
12
13
|
|
|
13
14
|
<div
|
|
14
|
-
class={cn(cardVariants({ padding }), className)}
|
|
15
|
+
class={cn(cardVariants({ variant, padding }), className)}
|
|
15
16
|
{...api.ariaProps}
|
|
16
17
|
{...api.dataAttributes}
|
|
17
18
|
{...props}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { createCardTitle, cardTitleVariants } from '../card/index.ts'
|
|
2
|
+
import { createCardTitle, cardTitleVariants, type CardTitleLevel } from '../card/index.ts'
|
|
3
3
|
import { cn } from '../shared/index.ts'
|
|
4
4
|
|
|
5
5
|
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
6
6
|
size?: 'sm' | 'default' | 'lg'
|
|
7
|
+
/** Heading element. Defaults to `h3`. */
|
|
8
|
+
as?: CardTitleLevel
|
|
7
9
|
}
|
|
8
10
|
|
|
9
|
-
const { size, class: className, ...props } = Astro.props
|
|
11
|
+
const { size, as: Heading = 'h3', class: className, ...props } = Astro.props
|
|
10
12
|
const api = createCardTitle()
|
|
11
13
|
---
|
|
12
14
|
|
|
13
|
-
<
|
|
15
|
+
<Heading class={cn(cardTitleVariants({ size }), className)} {...api.dataAttributes} {...props}>
|
|
14
16
|
<slot />
|
|
15
|
-
</
|
|
17
|
+
</Heading>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
computeFunnel,
|
|
4
|
+
funnelChartClasses as c,
|
|
5
|
+
funnelChartDefaults,
|
|
6
|
+
DEFAULT_FUNNEL_MIN_BAR_PERCENT,
|
|
7
|
+
type FunnelChartStep,
|
|
8
|
+
} from '../funnel-chart/index.ts'
|
|
9
|
+
import { cn } from '../shared/index.ts'
|
|
10
|
+
|
|
11
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
12
|
+
/** The funnel steps, top first. */
|
|
13
|
+
steps: FunnelChartStep[]
|
|
14
|
+
formatValue?: (value: number) => string
|
|
15
|
+
formatPercent?: (percent: number) => string
|
|
16
|
+
minBarPercent?: number
|
|
17
|
+
shareLabel?: (share: string) => string
|
|
18
|
+
conversionLabel?: (percent: string) => string
|
|
19
|
+
droppedLabel?: (count: string) => string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const {
|
|
23
|
+
steps,
|
|
24
|
+
formatValue = funnelChartDefaults.formatValue,
|
|
25
|
+
formatPercent = funnelChartDefaults.formatPercent,
|
|
26
|
+
minBarPercent = DEFAULT_FUNNEL_MIN_BAR_PERCENT,
|
|
27
|
+
shareLabel = funnelChartDefaults.shareLabel,
|
|
28
|
+
conversionLabel = funnelChartDefaults.conversionLabel,
|
|
29
|
+
droppedLabel = funnelChartDefaults.droppedLabel,
|
|
30
|
+
class: className,
|
|
31
|
+
...rest
|
|
32
|
+
} = Astro.props
|
|
33
|
+
const rows = computeFunnel(steps, minBarPercent)
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
<ol class={cn(c.root, className)} data-slot="funnel-chart" {...rest}>
|
|
37
|
+
{
|
|
38
|
+
rows.map((row) => (
|
|
39
|
+
<li>
|
|
40
|
+
<div class={c.bar}>
|
|
41
|
+
<div class={c.fill} style={`width:${row.barPercent}%`} aria-hidden="true" />
|
|
42
|
+
<svg class={c.chevron} width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
43
|
+
<path d="m9 18 6-6-6-6" />
|
|
44
|
+
</svg>
|
|
45
|
+
<div class={c.text}>
|
|
46
|
+
<b class={c.label}>{row.step.label}</b>
|
|
47
|
+
{row.step.description && <span class={c.description}>{row.step.description}</span>}
|
|
48
|
+
</div>
|
|
49
|
+
<div class={c.figure}>
|
|
50
|
+
<b class={c.value}>{formatValue(row.step.value)}</b>
|
|
51
|
+
<span class={c.share}>{shareLabel(formatPercent(row.shareOfTop))}</span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
{row.next && (
|
|
55
|
+
<div class={c.transition}>
|
|
56
|
+
<span class={c.conversion}>{conversionLabel(formatPercent(row.next.conversion))}</span>
|
|
57
|
+
<span aria-hidden="true">·</span>
|
|
58
|
+
<span class={c.dropped}>{droppedLabel(formatValue(row.next.dropped))}</span>
|
|
59
|
+
</div>
|
|
60
|
+
)}
|
|
61
|
+
</li>
|
|
62
|
+
))
|
|
63
|
+
}
|
|
64
|
+
</ol>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
computeLineChart,
|
|
4
|
+
lineChartClasses as c,
|
|
5
|
+
DEFAULT_LINE_CHART_WIDTH,
|
|
6
|
+
DEFAULT_LINE_CHART_HEIGHT,
|
|
7
|
+
type LineChartSeries,
|
|
8
|
+
} from '../line-chart/index.ts'
|
|
9
|
+
import { cn } from '../shared/index.ts'
|
|
10
|
+
|
|
11
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
12
|
+
/** The series to plot, all on one shared y-scale. */
|
|
13
|
+
series: LineChartSeries[]
|
|
14
|
+
/** One category label per data point. */
|
|
15
|
+
labels: string[]
|
|
16
|
+
/** Accessible name of the chart image. */
|
|
17
|
+
ariaLabel: string
|
|
18
|
+
height?: number
|
|
19
|
+
width?: number
|
|
20
|
+
/** Formats a y-axis tick. */
|
|
21
|
+
formatTick?: (value: number) => string
|
|
22
|
+
/** Fill the area under each line. Defaults to on for a single series only. */
|
|
23
|
+
area?: boolean
|
|
24
|
+
showLegend?: boolean
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
series,
|
|
29
|
+
labels,
|
|
30
|
+
ariaLabel,
|
|
31
|
+
height = DEFAULT_LINE_CHART_HEIGHT,
|
|
32
|
+
width = DEFAULT_LINE_CHART_WIDTH,
|
|
33
|
+
formatTick = (value: number) => String(Math.round(value)),
|
|
34
|
+
area,
|
|
35
|
+
showLegend = true,
|
|
36
|
+
class: className,
|
|
37
|
+
...rest
|
|
38
|
+
} = Astro.props
|
|
39
|
+
const geo = computeLineChart({ series, labels, width, height })
|
|
40
|
+
const fillArea = area ?? series.length === 1
|
|
41
|
+
const gradientBase = `line-chart-${ariaLabel.replace(/[^a-zA-Z0-9]+/g, '-').toLowerCase()}`
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
<div class={cn(c.root, className)} data-slot="line-chart" {...rest}>
|
|
45
|
+
<svg class={c.svg} viewBox={`0 0 ${width} ${height}`} style={`height:${height}px`} role="img" aria-label={ariaLabel}>
|
|
46
|
+
{geo.ticks.map((t) => (
|
|
47
|
+
<g>
|
|
48
|
+
<line class={c.gridline} x1={geo.padding.left} y1={t.y} x2={width - geo.padding.right} y2={t.y} />
|
|
49
|
+
<text class={c.axis} x={geo.padding.left - 8} y={t.y + 3} text-anchor="end">{formatTick(t.value)}</text>
|
|
50
|
+
</g>
|
|
51
|
+
))}
|
|
52
|
+
{geo.xLabels.map((l) => (
|
|
53
|
+
<text class={c.axis} x={l.x} y={height - 6} text-anchor="middle">{l.label}</text>
|
|
54
|
+
))}
|
|
55
|
+
{geo.paths.map((p, si) => (
|
|
56
|
+
<g>
|
|
57
|
+
{fillArea && (
|
|
58
|
+
<Fragment>
|
|
59
|
+
<defs>
|
|
60
|
+
<linearGradient id={`${gradientBase}-${si}`} x1="0" y1="0" x2="0" y2="1">
|
|
61
|
+
<stop offset="0%" stop-color={p.color} stop-opacity="0.22" />
|
|
62
|
+
<stop offset="100%" stop-color={p.color} stop-opacity="0" />
|
|
63
|
+
</linearGradient>
|
|
64
|
+
</defs>
|
|
65
|
+
<path d={p.area} fill={`url(#${gradientBase}-${si})`} />
|
|
66
|
+
</Fragment>
|
|
67
|
+
)}
|
|
68
|
+
<path d={p.line} fill="none" stroke={p.color} stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" />
|
|
69
|
+
</g>
|
|
70
|
+
))}
|
|
71
|
+
</svg>
|
|
72
|
+
{showLegend && (
|
|
73
|
+
<div class={c.legend}>
|
|
74
|
+
{series.map((s, si) => (
|
|
75
|
+
<span class={c.legendItem}>
|
|
76
|
+
<i class={c.legendSwatch} style={`background:${geo.paths[si].color}`} aria-hidden="true" />
|
|
77
|
+
{s.name}
|
|
78
|
+
</span>
|
|
79
|
+
))}
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
createPageHeader,
|
|
4
|
+
pageHeaderClass,
|
|
5
|
+
pageHeaderTextClass,
|
|
6
|
+
pageHeaderKickerClass,
|
|
7
|
+
pageHeaderTitleClass,
|
|
8
|
+
pageHeaderDescriptionClass,
|
|
9
|
+
pageHeaderActionsClass,
|
|
10
|
+
type PageHeaderTitleLevel,
|
|
11
|
+
} from '../page-header/index.ts'
|
|
12
|
+
import { cn } from '../shared/index.ts'
|
|
13
|
+
|
|
14
|
+
interface Props extends astroHTML.JSX.HTMLAttributes {
|
|
15
|
+
/** Optional eyebrow rendered above the title. */
|
|
16
|
+
kicker?: string
|
|
17
|
+
/** The page title. */
|
|
18
|
+
title: string
|
|
19
|
+
/** Optional supporting copy under the title (or use the `description` slot). */
|
|
20
|
+
description?: string
|
|
21
|
+
/** Heading element for the title. Defaults to `h1`. */
|
|
22
|
+
as?: PageHeaderTitleLevel
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { kicker, title, description, as: Title = 'h1', class: className, ...rest } = Astro.props
|
|
26
|
+
const hasActions = Astro.slots.has('actions')
|
|
27
|
+
const hasDescription = description != null || Astro.slots.has('description')
|
|
28
|
+
const { dataAttributes } = createPageHeader({ hasActions })
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
<header class={cn(pageHeaderClass, className)} {...dataAttributes} {...rest}>
|
|
32
|
+
<div class={pageHeaderTextClass}>
|
|
33
|
+
{kicker && <p class={pageHeaderKickerClass}>{kicker}</p>}
|
|
34
|
+
<Title class={pageHeaderTitleClass}>{title}</Title>
|
|
35
|
+
{hasDescription && (
|
|
36
|
+
<div class={pageHeaderDescriptionClass}>
|
|
37
|
+
<slot name="description">{description}</slot>
|
|
38
|
+
</div>
|
|
39
|
+
)}
|
|
40
|
+
</div>
|
|
41
|
+
{hasActions && (
|
|
42
|
+
<div class={pageHeaderActionsClass}>
|
|
43
|
+
<slot name="actions" />
|
|
44
|
+
</div>
|
|
45
|
+
)}
|
|
46
|
+
</header>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cva } from '../shared/index.ts'
|
|
2
2
|
|
|
3
3
|
export const badgeVariants = cva({
|
|
4
|
-
base: 'inline-flex items-center rounded-full border
|
|
4
|
+
base: 'inline-flex items-center rounded-full border font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
|
5
5
|
variants: {
|
|
6
6
|
variant: {
|
|
7
7
|
default: 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
|
|
@@ -11,6 +11,11 @@ export const badgeVariants = cva({
|
|
|
11
11
|
outline: 'text-foreground',
|
|
12
12
|
success: 'border-transparent bg-success text-success-foreground shadow hover:bg-success/80 font-semibold',
|
|
13
13
|
warning: 'border-transparent bg-warning text-warning-foreground shadow hover:bg-warning/80 font-semibold',
|
|
14
|
+
// Second brand accent + extended status roles (issue #485).
|
|
15
|
+
tertiary: 'border-transparent bg-tertiary text-tertiary-foreground shadow hover:bg-tertiary/80 font-semibold',
|
|
16
|
+
positive: 'border-transparent bg-positive text-positive-foreground shadow hover:bg-positive/80 font-semibold',
|
|
17
|
+
caution: 'border-transparent bg-caution text-caution-foreground shadow hover:bg-caution/80 font-semibold',
|
|
18
|
+
done: 'border-transparent bg-done text-info-foreground shadow hover:bg-done/80 font-semibold',
|
|
14
19
|
},
|
|
15
20
|
size: {
|
|
16
21
|
sm: 'px-2 py-0 text-[10px]',
|
package/dist/badge/badge.ts
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import type { AccessibilityProps } from '../shared/index.ts'
|
|
2
2
|
|
|
3
|
-
export type BadgeVariant =
|
|
3
|
+
export type BadgeVariant =
|
|
4
|
+
| 'default'
|
|
5
|
+
| 'primary'
|
|
6
|
+
| 'secondary'
|
|
7
|
+
| 'destructive'
|
|
8
|
+
| 'outline'
|
|
9
|
+
| 'success'
|
|
10
|
+
| 'warning'
|
|
11
|
+
// Issue #485 — second brand accent + extended status roles.
|
|
12
|
+
| 'tertiary'
|
|
13
|
+
| 'positive'
|
|
14
|
+
| 'caution'
|
|
15
|
+
| 'done'
|
|
4
16
|
export type BadgeSize = 'sm' | 'md'
|
|
5
17
|
|
|
6
18
|
export interface BadgeProps {
|
|
@@ -15,13 +27,23 @@ export interface BadgeAPI {
|
|
|
15
27
|
dataAttributes: Record<string, string>
|
|
16
28
|
}
|
|
17
29
|
|
|
18
|
-
const statusVariants: BadgeVariant[] = [
|
|
30
|
+
const statusVariants: BadgeVariant[] = [
|
|
31
|
+
'success',
|
|
32
|
+
'warning',
|
|
33
|
+
'destructive',
|
|
34
|
+
'positive',
|
|
35
|
+
'caution',
|
|
36
|
+
'done',
|
|
37
|
+
]
|
|
19
38
|
|
|
20
39
|
/** Icon name for status badge variants — non-color indicator for colorblind safety */
|
|
21
40
|
const BADGE_ICONS: Partial<Record<BadgeVariant, string>> = {
|
|
22
41
|
success: 'check',
|
|
23
42
|
warning: 'alert',
|
|
24
43
|
destructive: 'x',
|
|
44
|
+
positive: 'check',
|
|
45
|
+
caution: 'alert',
|
|
46
|
+
done: 'check',
|
|
25
47
|
}
|
|
26
48
|
|
|
27
49
|
/** Get the icon name for a badge variant (undefined for non-status variants) */
|
|
@@ -13,7 +13,7 @@ export const buttonTokens: TokenContract = {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const buttonVariants = cva({
|
|
16
|
-
base: 'inline-flex items-center justify-center gap-2 whitespace-nowrap
|
|
16
|
+
base: 'inline-flex items-center justify-center gap-2 whitespace-nowrap font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0',
|
|
17
17
|
variants: {
|
|
18
18
|
variant: {
|
|
19
19
|
default: 'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
|
@@ -24,15 +24,21 @@ export const buttonVariants = cva({
|
|
|
24
24
|
destructive: 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
|
25
25
|
outline: 'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
|
|
26
26
|
secondary: 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
|
|
27
|
+
// Soft tinted brand fill — lower-emphasis primary action (issue #485).
|
|
28
|
+
soft: 'bg-primary-soft text-primary-soft-foreground shadow-sm hover:bg-primary-soft/80',
|
|
29
|
+
// Second brand accent (issue #485).
|
|
30
|
+
tertiary: 'bg-tertiary text-tertiary-foreground shadow-sm hover:bg-tertiary/90',
|
|
27
31
|
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
28
32
|
link: 'text-primary underline-offset-4 hover:underline',
|
|
29
33
|
},
|
|
30
34
|
size: {
|
|
31
35
|
xs: 'h-7 rounded px-2 text-xs',
|
|
32
36
|
sm: 'h-8 rounded-md px-3 text-xs',
|
|
33
|
-
default: 'h-9 px-4 py-2',
|
|
34
|
-
lg: 'h-10 rounded-md px-8',
|
|
35
|
-
icon: 'h-9 w-9',
|
|
37
|
+
default: 'h-9 rounded-md px-4 py-2 text-sm',
|
|
38
|
+
lg: 'h-10 rounded-md px-8 text-sm',
|
|
39
|
+
icon: 'h-9 w-9 rounded-md text-sm',
|
|
40
|
+
'icon-sm': 'h-8 w-8 rounded-md text-xs',
|
|
41
|
+
'icon-xs': 'h-7 w-7 rounded text-xs',
|
|
36
42
|
},
|
|
37
43
|
},
|
|
38
44
|
defaultVariants: {
|
package/dist/button/button.ts
CHANGED
|
@@ -14,6 +14,9 @@ export type ButtonVariant =
|
|
|
14
14
|
| 'secondary'
|
|
15
15
|
| 'ghost'
|
|
16
16
|
| 'link'
|
|
17
|
+
// Issue #485 — soft tinted brand fill + a real second brand accent.
|
|
18
|
+
| 'soft'
|
|
19
|
+
| 'tertiary'
|
|
17
20
|
|
|
18
21
|
/** Variants that map to the same visual style as `default`. */
|
|
19
22
|
export const BUTTON_VARIANT_ALIASES: Partial<Record<ButtonVariant, ButtonVariant>> = {
|
|
@@ -25,7 +28,7 @@ export function resolveButtonVariant(variant: ButtonVariant | undefined): Button
|
|
|
25
28
|
if (!variant) return variant
|
|
26
29
|
return BUTTON_VARIANT_ALIASES[variant] ?? variant
|
|
27
30
|
}
|
|
28
|
-
export type ButtonSize = 'xs' | 'sm' | 'default' | 'lg' | 'icon'
|
|
31
|
+
export type ButtonSize = 'xs' | 'sm' | 'default' | 'lg' | 'icon' | 'icon-sm' | 'icon-xs'
|
|
29
32
|
|
|
30
33
|
export interface ButtonProps {
|
|
31
34
|
variant?: ButtonVariant
|
package/dist/callout/callout.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import type { AccessibilityProps } from '../shared/index.ts'
|
|
2
2
|
|
|
3
3
|
export interface CalloutProps {
|
|
4
|
-
/**
|
|
4
|
+
/** Explicit role; always wins. */
|
|
5
5
|
role?: string
|
|
6
|
+
/** Destructive callouts are announced (`role="alert"`). */
|
|
7
|
+
destructive?: boolean
|
|
8
|
+
/**
|
|
9
|
+
* Whether the callout has an accessible name (`aria-label` /
|
|
10
|
+
* `aria-labelledby`). Only a named callout becomes a `region` landmark — an
|
|
11
|
+
* unnamed region is an axe violation and landmark noise.
|
|
12
|
+
*/
|
|
13
|
+
labelled?: boolean
|
|
6
14
|
}
|
|
7
15
|
|
|
8
16
|
export interface CalloutAPI {
|
|
@@ -13,10 +21,11 @@ export interface CalloutAPI {
|
|
|
13
21
|
export function createCallout(props: CalloutProps = {}): CalloutAPI {
|
|
14
22
|
const ariaProps: Partial<AccessibilityProps> = {}
|
|
15
23
|
|
|
16
|
-
// Callouts are usually alert or region depending on severity
|
|
17
24
|
if (props.role) {
|
|
18
25
|
ariaProps.role = props.role
|
|
19
|
-
} else {
|
|
26
|
+
} else if (props.destructive) {
|
|
27
|
+
ariaProps.role = 'alert'
|
|
28
|
+
} else if (props.labelled) {
|
|
20
29
|
ariaProps.role = 'region'
|
|
21
30
|
}
|
|
22
31
|
|
package/dist/card/card.styles.ts
CHANGED
|
@@ -11,8 +11,16 @@ export const cardTokens: TokenContract = {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export const cardVariants = cva({
|
|
14
|
-
base: 'rounded-lg border
|
|
14
|
+
base: 'rounded-lg border shadow',
|
|
15
15
|
variants: {
|
|
16
|
+
variant: {
|
|
17
|
+
// Default — unchanged surface (bg-card + border via the global token).
|
|
18
|
+
default: 'bg-card text-card-foreground',
|
|
19
|
+
// Lower-emphasis container on the subtle surface token (issue #485).
|
|
20
|
+
subtle: 'bg-surface-subtle text-card-foreground',
|
|
21
|
+
// Tinted second-accent card (issue #485).
|
|
22
|
+
tertiary: 'border-transparent bg-tertiary-soft text-tertiary-soft-foreground',
|
|
23
|
+
},
|
|
16
24
|
padding: {
|
|
17
25
|
none: '',
|
|
18
26
|
default: 'p-6',
|
|
@@ -20,6 +28,7 @@ export const cardVariants = cva({
|
|
|
20
28
|
},
|
|
21
29
|
},
|
|
22
30
|
defaultVariants: {
|
|
31
|
+
variant: 'default',
|
|
23
32
|
padding: 'none',
|
|
24
33
|
},
|
|
25
34
|
})
|
package/dist/card/card.ts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import type { AccessibilityProps } from '../shared/index.ts'
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Visual tone of a card (issue #485).
|
|
5
|
+
* - `default` — the standard surface (activates the `--rfr-card-*` contract).
|
|
6
|
+
* - `subtle` — a lower-emphasis container on the subtle surface token.
|
|
7
|
+
* - `tertiary` — a tinted second-brand-accent card.
|
|
8
|
+
*/
|
|
9
|
+
export type CardVariant = 'default' | 'subtle' | 'tertiary'
|
|
10
|
+
|
|
11
|
+
/** Built-in inner padding of the Card container. */
|
|
12
|
+
export type CardPadding = 'none' | 'default' | 'compact'
|
|
13
|
+
|
|
14
|
+
/** Heading element for CardTitle. */
|
|
15
|
+
export type CardTitleLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
|
|
16
|
+
|
|
3
17
|
export interface CardProps {
|
|
4
18
|
/** Optional accessible role override */
|
|
5
19
|
role?: string
|
package/dist/card/index.ts
CHANGED
|
@@ -9,6 +9,9 @@ export const diffViewerTokens: TokenContract = {
|
|
|
9
9
|
sidebarBg: { variable: '--rfr-diff-sidebar-bg', fallback: 'hsl(var(--muted))' },
|
|
10
10
|
headerBg: { variable: '--rfr-diff-header-bg', fallback: 'hsl(var(--muted))' },
|
|
11
11
|
border: { variable: '--rfr-diff-border', fallback: 'hsl(var(--border))' },
|
|
12
|
+
// Diff add/del colors are syntax semantics (like code highlighting), not
|
|
13
|
+
// theme status tones — deliberately NOT routed through the success/
|
|
14
|
+
// destructive tokens. The --rfr-diff-* variables are the theming seam.
|
|
12
15
|
addBg: { variable: '--rfr-diff-add-bg', fallback: 'rgba(46, 160, 67, 0.15)' },
|
|
13
16
|
delBg: { variable: '--rfr-diff-del-bg', fallback: 'rgba(248, 81, 73, 0.15)' },
|
|
14
17
|
addFg: { variable: '--rfr-diff-add-fg', fallback: '#3fb950' },
|