Skip to content

Commit 158ed6f

Browse files
committed
[Toolkit][Shadcn] Add marker recipe
1 parent 565e9f1 commit 158ed6f

18 files changed

Lines changed: 675 additions & 0 deletions

src/Toolkit/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- [Shadcn] Add `sheet` recipe
1818
- [Shadcn] Add `form` recipe
1919
- [Shadcn] Add `sidebar` recipe
20+
- [Shadcn] Add `marker` recipe
2021
- [Shadcn][Flowbite] Merge component classes with `attributes.defaults({ class: '...'|tailwind_classes })` instead of `tailwind_merge` (consumer classes now override component variants)
2122
- [Shadcn] Fix Field/Label, InputGroup and Pagination silently dropping their own base classes when forwarding to a child component (their checked/disabled/nested/dark styles now apply)
2223
- [Shadcn] Fix `dialog` opening on initial render when `open` is `false`
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
# Marker
2+
3+
Displays an inline status, system note, bordered row, or labeled separator in a conversation.
4+
5+
```twig {"preview":true,"height":"300px"}
6+
<div class="flex w-full max-w-sm flex-col gap-8 py-12">
7+
<twig:Marker>
8+
<twig:Marker:Icon>
9+
<twig:ux:icon name="lucide:git-branch" />
10+
</twig:Marker:Icon>
11+
<twig:Marker:Content>Switched to a new branch</twig:Marker:Content>
12+
</twig:Marker>
13+
<twig:Marker role="status">
14+
<twig:Marker:Icon>
15+
<twig:Spinner />
16+
</twig:Marker:Icon>
17+
<twig:Marker:Content class="animate-pulse">Thinking...</twig:Marker:Content>
18+
</twig:Marker>
19+
<twig:Marker variant="separator">
20+
<twig:Marker:Content>Conversation compacted</twig:Marker:Content>
21+
</twig:Marker>
22+
<twig:Marker>
23+
<twig:Marker:Icon>
24+
<twig:ux:icon name="lucide:search" />
25+
</twig:Marker:Icon>
26+
<twig:Marker:Content>Explored 4 files</twig:Marker:Content>
27+
</twig:Marker>
28+
</div>
29+
```
30+
31+
## Installation
32+
33+
::: installation
34+
35+
## Usage
36+
37+
```twig
38+
<twig:Marker variant="default">
39+
<twig:Marker:Icon>
40+
<twig:ux:icon name="lucide:check" />
41+
</twig:Marker:Icon>
42+
<twig:Marker:Content>Explored 4 files</twig:Marker:Content>
43+
</twig:Marker>
44+
```
45+
46+
`Marker` is presentational by default. Set `role="status"` for streaming or in-progress markers so assistive technologies announce the update, and use the `as` prop to render a real `a` or `button` when the marker is interactive.
47+
48+
## Examples
49+
50+
### Variants
51+
52+
Use the `variant` prop to switch between an inline marker, a bordered row, and a labeled separator.
53+
54+
```twig {"preview":true,"height":"240px"}
55+
<div class="flex w-full max-w-sm flex-col gap-8 py-12">
56+
<twig:Marker>
57+
<twig:Marker:Content>A default marker for inline notes.</twig:Marker:Content>
58+
</twig:Marker>
59+
<twig:Marker variant="separator">
60+
<twig:Marker:Content>A separator marker</twig:Marker:Content>
61+
</twig:Marker>
62+
<twig:Marker variant="border">
63+
<twig:Marker:Content>A border marker for row boundaries.</twig:Marker:Content>
64+
</twig:Marker>
65+
</div>
66+
```
67+
68+
### Status
69+
70+
Set `role="status"` and include a `Spinner` for streaming or in-progress markers so updates are announced.
71+
72+
```twig {"preview":true,"height":"200px"}
73+
<div class="flex w-full max-w-sm flex-col gap-8 py-12">
74+
<twig:Marker role="status">
75+
<twig:Marker:Icon>
76+
<twig:Spinner />
77+
</twig:Marker:Icon>
78+
<twig:Marker:Content>Compacting conversation</twig:Marker:Content>
79+
</twig:Marker>
80+
<twig:Marker variant="separator" role="status">
81+
<twig:Marker:Icon>
82+
<twig:Spinner />
83+
</twig:Marker:Icon>
84+
<twig:Marker:Content>Running tests</twig:Marker:Content>
85+
</twig:Marker>
86+
</div>
87+
```
88+
89+
### Shimmer
90+
91+
Add an animation class to `Marker:Content` for a streaming-text effect. Shadcn ships a dedicated `shimmer` utility in its own package; with plain Tailwind, `animate-pulse` gives the same in-progress cue.
92+
93+
```twig {"preview":true,"height":"200px"}
94+
<div class="flex w-full max-w-sm flex-col gap-8 py-12">
95+
<twig:Marker role="status">
96+
<twig:Marker:Content class="animate-pulse">Thinking...</twig:Marker:Content>
97+
</twig:Marker>
98+
<twig:Marker variant="separator" role="status">
99+
<twig:Marker:Content class="animate-pulse">Reading 4 files</twig:Marker:Content>
100+
</twig:Marker>
101+
</div>
102+
```
103+
104+
### Separator
105+
106+
Use the `separator` variant for labeled dividers, such as dates or section breaks, in a conversation.
107+
108+
```twig {"preview":true,"height":"240px"}
109+
<div class="flex w-full max-w-sm flex-col gap-8 py-12">
110+
<twig:Marker variant="separator">
111+
<twig:Marker:Content>Today</twig:Marker:Content>
112+
</twig:Marker>
113+
<twig:Marker variant="separator">
114+
<twig:Marker:Content>Worked for 42s</twig:Marker:Content>
115+
</twig:Marker>
116+
<twig:Marker variant="separator">
117+
<twig:Marker:Content>Conversation compacted</twig:Marker:Content>
118+
</twig:Marker>
119+
</div>
120+
```
121+
122+
A labeled separator needs no role: the divider lines are decorative CSS pseudo-elements and the text is announced as ordinary content. Do not add `role="separator"` — it takes its accessible name from `aria-label`, so the visible label would not be announced.
123+
124+
### Border
125+
126+
Use the `border` variant for status rows that should keep the default marker alignment while separating the next row.
127+
128+
```twig {"preview":true,"height":"230px"}
129+
<div class="flex w-full max-w-sm flex-col gap-3 py-12">
130+
<twig:Marker variant="border">
131+
<twig:Marker:Icon>
132+
<twig:ux:icon name="lucide:git-branch" />
133+
</twig:Marker:Icon>
134+
<twig:Marker:Content>Switched to release-candidate</twig:Marker:Content>
135+
</twig:Marker>
136+
<twig:Marker variant="border">
137+
<twig:Marker:Icon>
138+
<twig:ux:icon name="lucide:search" />
139+
</twig:Marker:Icon>
140+
<twig:Marker:Content>Reviewed 8 related files</twig:Marker:Content>
141+
</twig:Marker>
142+
<twig:Marker variant="border">
143+
<twig:Marker:Icon>
144+
<twig:ux:icon name="lucide:file-text" />
145+
</twig:Marker:Icon>
146+
<twig:Marker:Content>Opened implementation notes</twig:Marker:Content>
147+
</twig:Marker>
148+
</div>
149+
```
150+
151+
### With Icon
152+
153+
Use `Marker:Icon` to render an icon alongside the content. It is decorative and hidden from assistive technologies, so the adjacent `Marker:Content` carries the meaning. Use `flex-col` to stack the icon above the content.
154+
155+
```twig {"preview":true,"height":"300px"}
156+
<div class="flex w-full max-w-sm flex-col gap-12 py-12">
157+
<twig:Marker>
158+
<twig:Marker:Icon>
159+
<twig:ux:icon name="lucide:git-branch" />
160+
</twig:Marker:Icon>
161+
<twig:Marker:Content>Switched to a new branch</twig:Marker:Content>
162+
</twig:Marker>
163+
<twig:Marker variant="separator">
164+
<twig:Marker:Icon>
165+
<twig:ux:icon name="lucide:search" />
166+
</twig:Marker:Icon>
167+
<twig:Marker:Content>Explored 4 files</twig:Marker:Content>
168+
</twig:Marker>
169+
<twig:Marker class="flex-col">
170+
<twig:Marker:Icon>
171+
<twig:ux:icon name="lucide:book-open-check" />
172+
</twig:Marker:Icon>
173+
<twig:Marker:Content>Syncing completed</twig:Marker:Content>
174+
</twig:Marker>
175+
</div>
176+
```
177+
178+
### Links and Buttons
179+
180+
Turn a marker into a link or a button with the `as` prop on `Marker`, so it is focusable and exposes the correct role. The accessible name comes from the marker text.
181+
182+
```twig {"preview":true,"height":"200px"}
183+
<div class="flex w-full max-w-sm flex-col gap-8 py-12">
184+
<twig:Marker as="a" href="#links-and-buttons">
185+
<twig:Marker:Icon>
186+
<twig:ux:icon name="lucide:git-branch" />
187+
</twig:Marker:Icon>
188+
<twig:Marker:Content>View the pull request</twig:Marker:Content>
189+
</twig:Marker>
190+
<twig:Marker as="button" type="button" class="transition-colors hover:text-foreground">
191+
<twig:Marker:Icon>
192+
<twig:ux:icon name="lucide:rotate-ccw" />
193+
</twig:Marker:Icon>
194+
<twig:Marker:Content>Revert this change</twig:Marker:Content>
195+
</twig:Marker>
196+
</div>
197+
```
198+
199+
### RTL
200+
201+
To enable RTL support, set the `dir="rtl"` attribute on the root element.
202+
203+
```twig {"preview":true,"height":"420px"}
204+
<div class="flex w-full flex-col items-center gap-4">
205+
{# Arabic #}
206+
<div class="flex w-full max-w-sm flex-col gap-6 py-6" dir="rtl">
207+
<twig:Marker>
208+
<twig:Marker:Icon>
209+
<twig:ux:icon name="lucide:git-branch" />
210+
</twig:Marker:Icon>
211+
<twig:Marker:Content>تم التبديل إلى فرع جديد</twig:Marker:Content>
212+
</twig:Marker>
213+
<twig:Marker variant="separator">
214+
<twig:Marker:Content>تم ضغط المحادثة</twig:Marker:Content>
215+
</twig:Marker>
216+
<twig:Marker variant="border">
217+
<twig:Marker:Icon>
218+
<twig:ux:icon name="lucide:search" />
219+
</twig:Marker:Icon>
220+
<twig:Marker:Content>تم استكشاف 4 ملفات</twig:Marker:Content>
221+
</twig:Marker>
222+
</div>
223+
224+
{# Hebrew #}
225+
<div class="flex w-full max-w-sm flex-col gap-6 py-6" dir="rtl">
226+
<twig:Marker>
227+
<twig:Marker:Icon>
228+
<twig:ux:icon name="lucide:git-branch" />
229+
</twig:Marker:Icon>
230+
<twig:Marker:Content>עברת לענף חדש</twig:Marker:Content>
231+
</twig:Marker>
232+
<twig:Marker variant="separator">
233+
<twig:Marker:Content>השיחה כווצה</twig:Marker:Content>
234+
</twig:Marker>
235+
<twig:Marker variant="border">
236+
<twig:Marker:Icon>
237+
<twig:ux:icon name="lucide:search" />
238+
</twig:Marker:Icon>
239+
<twig:Marker:Content>נסרקו 4 קבצים</twig:Marker:Content>
240+
</twig:Marker>
241+
</div>
242+
</div>
243+
```
244+
245+
## API Reference
246+
247+
::: api-reference
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"$schema": "../../../schema-kit-recipe-v1.json",
3+
"type": "component",
4+
"name": "Marker",
5+
"version-added": "3.5",
6+
"copy-files": {
7+
"templates/": "templates/"
8+
},
9+
"dependencies": {
10+
"composer": [
11+
"twig/extra-bundle",
12+
"twig/html-extra:^3.24.0",
13+
"symfony/ux-twig-component:^3.5",
14+
"tales-from-a-dev/twig-tailwind-extra:^1.3.0"
15+
]
16+
}
17+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{# @prop variant 'default'|'separator'|'border' The visual style variant. #}
2+
{# @prop as 'div'|'a'|'button' The HTML tag to render. #}
3+
{# @block content The marker content, typically includes `Marker:Icon` and `Marker:Content`. #}
4+
{%- props variant = 'default', as = 'div' -%}
5+
{%- set style = html_cva(
6+
base: "group/marker relative flex min-h-4 w-full items-center gap-2 ltr:text-left rtl:text-start text-sm text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [a]:underline [a]:underline-offset-3 [a]:hover:text-foreground",
7+
variants: {
8+
variant: {
9+
default: '',
10+
separator: 'ltr:before:mr-1 rtl:before:me-1 before:h-px before:min-w-0 before:flex-1 before:bg-border ltr:after:ml-1 rtl:after:ms-1 after:h-px after:min-w-0 after:flex-1 after:bg-border',
11+
border: 'border-b border-border pb-2',
12+
},
13+
},
14+
) -%}
15+
<{{ as }}
16+
data-slot="marker"
17+
data-variant="{{ variant }}"
18+
{{ attributes.defaults({
19+
class: style.apply({variant: variant})|tailwind_classes,
20+
}) }}
21+
>
22+
{%- block content %}{% endblock -%}
23+
</{{ as }}>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{# @block content The marker text content. #}
2+
<span
3+
data-slot="marker-content"
4+
{{ attributes.defaults({
5+
class: 'min-w-0 wrap-break-word group-data-[variant=separator]/marker:flex-none group-data-[variant=separator]/marker:text-center *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground'|tailwind_classes,
6+
}) }}
7+
>
8+
{%- block content %}{% endblock -%}
9+
</span>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{# @block content The decorative icon rendered inside the marker. #}
2+
<span
3+
data-slot="marker-icon"
4+
aria-hidden="true"
5+
{{ attributes.defaults({
6+
class: "size-4 shrink-0 [&_svg:not([class*='size-'])]:size-4"|tailwind_classes,
7+
}) }}
8+
>
9+
{%- block content %}{% endblock -%}
10+
</span>
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading

src/Toolkit/tests/Functional/__snapshots__/ComponentsRenderingTest__testComponentRendering with data set Kit shadcn, component marker, example 0__1.html

Lines changed: 51 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)