Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 28 additions & 34 deletions apps/docs/content/1.component/svg-draw.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ icon: pen-tool

Wrap **any inline SVG**, an icon, a logo or a whole illustration, and it
**draws itself in**. Every path, line, circle, rect and polygon **traces its
outline** one after another, longest first, then its **fill fades in** behind
the line. Outline icons draw in their own stroke and keep it; filled shapes are
traced in a thin line that fades away as they fill. Once a shape settles it is
**handed back exactly as written**. It plays when the SVG scrolls into view, or
on mount. Plain CSS transitions, no animation library.
outline** one after another, then its **fill fades in** behind the line.
Outline icons draw in their own stroke and keep it; filled shapes are traced in
a thin line that fades away as they fill. It plays when the SVG scrolls into
view, or on mount. GSAP DrawSVG and ScrollTrigger.

::component-preview{name="svg-draw" size="lg"}
::
Expand Down Expand Up @@ -66,43 +65,38 @@ pasted from a design tool, or a whole illustration. Tune the pace with

## Props

| Prop | Type | Default | Description |
| ------------ | -------------------------------------- | ---------------- | --------------------------------------------------------------------------- |
| `duration` | `number` | `1.6` | Seconds each shape takes to draw. |
| `delay` | `number` | `0` | Seconds before the first shape starts. |
| `stagger` | `number` | `0.18` | Seconds between one shape starting and the next. |
| `order` | `'longest' \| 'reverse' \| 'document'` | `'longest'` | Which shapes go first. |
| `ease` | `SvgDrawEase \| string` | `'power1.out'` | A preset or any CSS easing. |
| `width` | `number` | `1.5` | Trace width in screen px for fill-only shapes. |
| `color` | `string` | `'currentColor'` | Trace color for fill-only shapes. |
| `ownColor` | `boolean` | `false` | Trace fill-only shapes in their own fill color instead. |
| `trigger` | `'view' \| 'mount'` | `'view'` | Play on scrolling into view, or straight away. |
| `startAt` | `number` | `0.85` | With `view`: how far down the viewport the SVG's top must rise, `0` to `1`. |
| `onComplete` | `() => void` | — | Called once every shape is drawn and settled. |

The presets are `'power1.out'`, `'power2.out'`, `'power3.out'`, `'expo.out'`,
`'in-out'` and `'linear'`. It renders a `<div>` around your SVG and extends
native `<div>` attributes (`className`, `style`, …).
| Prop | Type | Default | Description |
| ------------ | ------------------- | ---------------- | ------------------------------------------------ |
| `duration` | `number` | `1.4` | Seconds each shape takes to draw. |
| `delay` | `number` | `0` | Seconds before the first shape starts. |
| `stagger` | `number` | `0.12` | Seconds between one shape starting and the next. |
| `ease` | `string` | `'power2.inOut'` | Any GSAP ease. |
| `width` | `number` | `1.5` | Trace width in screen px for fill-only shapes. |
| `trigger` | `'view' \| 'mount'` | `'view'` | Play on scrolling into view, or straight away. |
| `start` | `string` | `'top 85%'` | ScrollTrigger start, with `trigger="view"`. |
| `onComplete` | `() => void` | — | Called once every shape is drawn and filled. |

The trace line takes the wrapper's text color, so set it with a `text-*`
class. It renders a `<div>` around your SVG and extends native `<div>`
attributes (`className`, `style`, …).

## Behavior

- **Any shape, any SVG.** It finds every `path`, `line`, `polyline`,
`polygon`, `circle`, `ellipse` and `rect`, however deeply nested in groups
or across several SVGs. Shapes inside `defs`, `clipPath`, `mask`,
`pattern`, `marker`, `symbol` and gradients are skipped, because they are
never painted where they sit, and so are hidden shapes.
`pattern`, `marker` and `symbol` are skipped, because they are never painted
where they sit.
- **Strokes stay strokes, fills stay fills.** A stroked shape draws in its own
stroke color and width and keeps them. A fill-only shape draws in a thin
`color` line that fades out as its fill fades in. Colors are never swapped:
every fill ends in its own color, gradients included.
- **The same line on any scale.** `width` is in screen pixels, so the trace
looks the same on a 24-unit icon and on a 1000-unit logo.
- **Shapes that set `pathLength`** are measured in their own units, so the
line still runs exactly from start to end.
- **Handed back untouched.** Once a shape has settled, its own `style`
attribute is put back exactly as it was, so nothing lingers to fight your
CSS. Unmounting mid-draw, a prop change or React Strict Mode's double effect
restores the SVG too, so it is always measured as written.
line in the text color that fades out as its fill fades in. Every fill ends
in its own color and opacity, gradients included.
- **The same line on any scale.** The trace uses a non-scaling stroke, so
`width` looks the same on a 24-unit icon and on a 1000-unit logo.
- **Measured by DrawSVG.** Path lengths, `pathLength` attributes and line
caps are all handled by GSAP's DrawSVG plugin.
- **Cleans up after itself.** The timeline runs inside `useGSAP`, so
unmounting or changing a prop reverts every shape to how it was written.
- **No flash.** Where scripts run, the SVG is hidden until its first frame is
set up, before paint. Without JavaScript it simply shows.
- **Only inline SVG.** An `<img src="logo.svg">` or a CSS background cannot be
Expand Down
60 changes: 26 additions & 34 deletions apps/docs/content/2.block/arc-carousel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ downward arc**, each one further from the centre sitting a little lower and
**turned a little more**, three to a side. **Step** and the whole arc swings
round: every card glides to its neighbour's slot on a slow, weighted curve,
while the one arriving in the middle **eases up in size** and takes a **lime
outline**. A card that wraps past the edge **keeps travelling off-screen**
while it comes back in **from the far side**, so the loop never snaps.
**Swipe**, **drag**, use the **arrow keys**, or **click a side card** to bring
it to the centre. Plain CSS transitions, no animation library.
outline**. Cards fade out past the third slot and wrap round unseen, so the
loop never snaps. **Swipe**, **drag**, use the **arrow keys**, or **click a
side card** to bring it to the centre. GSAP with ScrollTrigger.

::component-preview{name="arc-carousel" size="xl"}
::
Expand Down Expand Up @@ -50,19 +49,16 @@ export default function Page() {
:::
::

It ships with eight sample projects. Pass your own as `items`, and point the
button wherever the full list lives:
It ships with eight sample projects. Pass your own as `items`, and put your
own heading above it:

```tsx [app/page.tsx]
import ArcCarousel from '@/components/tweenui/arc-carousel';

export default function Page() {
return (
<ArcCarousel
badge="Just in"
title="New this season."
description="Fresh drops from the workshop, restocked every Friday."
cta={{ label: 'Shop the collection', href: '/shop' }}
label="New this season"
items={[
{
title: 'Field Jacket',
Expand All @@ -85,14 +81,11 @@ export default function Page() {
}
```

The colors are the Tween UI theme tokens, used as Tailwind classes rather than
raw hex: `tween-ink-deep` (`#03110f`) for the stage and the edge fades,
`tween-ink` (`#071d1b`) under each photo, a `tween-teal` (`#045f64`) glow
behind the arc, `tween-teal-soft` (`#9fd4d6`) for the supporting text and
`tween-lime` (`#c6f56f`) on the badge, the centre card's outline and arrow,
and the button. The CLI adds them to your `@theme`; with a manual install,
copy the CSS block from the Manual tab into your global stylesheet. Change a
token there and the block follows.
The colors are the Tween UI palette, written inline as Tailwind classes so
there is nothing to add to your theme: the deep ink stage (`#03110f`), card
surfaces in `#071d1b`, a teal glow (`#045f64`), soft teal text (`#9fd4d6`) and
the lime accent (`#c6f56f`) on the centre card and the controls. Restyle them
in the `className` strings.

### Autoplay

Expand All @@ -108,15 +101,12 @@ current card's time runs out. Set the pace with `interval`, or turn it off with

## Props

| Prop | Type | Default | Description |
| ------------- | ----------------------------------------- | ------------------------------------ | -------------------------------------------------- |
| `items` | `ArcCarouselItem[]` | Eight sample projects | The cards on the arc. |
| `badge` | `string` | `'Featured work'` | Pill above the title. Pass `''` to drop it. |
| `title` | `string` | `'Projects we shipped this season.'` | Heading, and the carousel's accessible name. |
| `description` | `string` | How to browse | Line under the heading. Pass `''` to drop it. |
| `cta` | `{ label: string; href: string } \| null` | `View all projects` | Button between the arrows. Pass `null` to drop it. |
| `autoplay` | `boolean` | `true` | Step to the next card on its own. |
| `interval` | `number` | `5000` | Time each card holds the centre, in ms. |
| Prop | Type | Default | Description |
| ---------- | ------------------- | --------------------- | --------------------------------------- |
| `items` | `ArcCarouselItem[]` | Eight sample projects | The cards on the arc. |
| `label` | `string` | `'Featured projects'` | Accessible name for the carousel. |
| `autoplay` | `boolean` | `true` | Step to the next card on its own. |
| `interval` | `number` | `5000` | Time each card holds the centre, in ms. |

Each item is `{ title, image, tag?, meta?, href? }`. The `tag` is the chip on
the top left of the card and `meta` the line under the title. With an `href`
Expand All @@ -129,10 +119,11 @@ attributes (`className`, `style`, …).
fixed offsets from the centre, so the curve keeps its shape whatever the
content. Below 1024px of container width the whole arc scales to 80%, and
below 768px to 62%, so it fits a phone without relaying out.
- **Wraps without a jump.** When a card has to move from one end of the loop
to the other, a copy of it carries on off the near edge and fades, while
the card itself is placed past the far edge and slides in. The copy is
removed once its move has finished.
- **One number drives the arc.** GSAP tweens the carousel's position, and
every card is placed from it each frame, sliding smoothly between slots.
Past the third slot a card fades out, so it crosses from one end of the
loop to the other while invisible. With eight or more items that crossing
is never seen.
- **Side cards step, the centre card opens.** A click on a side card moves
the arc one step toward it instead of following its link, and a swipe or
drag of more than 50px sideways steps it the other way and swallows the
Expand All @@ -148,8 +139,9 @@ attributes (`className`, `style`, …).
nearest the centre load first and the rest lazily.
- **Autoplay waits for you.** It holds while the mouse rests on the cards,
while the cards have keyboard focus and while the block is off-screen, and
picks up where it left off. Stepping by hand gives the new card a full
interval. A backgrounded tab never makes it skip ahead on return.
picks up where it left off. The progress ring is the clock: one GSAP
tween that pauses and resumes. Stepping by hand gives the new card a full
interval.
- **It is in place before it runs.** The first layout is written into the
markup, so the arc renders already curved and nothing flashes into place
on load.
Expand All @@ -158,7 +150,7 @@ attributes (`className`, `style`, …).

::note
The carousel is a focusable group with `aria-roledescription="carousel"`,
named by the title, and the left and right arrow keys step it while it has
named by `label`, and the left and right arrow keys step it while it has
focus. Each card is a slide labelled `n of total: title`; cards off the
centre are hidden from screen readers and out of the tab order, so only the
card you are looking at can be reached. The current card is announced
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/public/r/arc-carousel-demo.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
{
"path": "examples/arc-carousel-demo-2.tsx",
"type": "registry:example",
"content": "import ArcCarousel from '@/components/tweenui/arc-carousel';\n\nexport default function Page() {\n return (\n <ArcCarousel\n badge=\"Just in\"\n title=\"New this season.\"\n description=\"Fresh drops from the workshop, restocked every Friday.\"\n cta={{ label: 'Shop the collection', href: '/shop' }}\n items={[\n {\n title: 'Field Jacket',\n tag: 'Outerwear',\n meta: '$240',\n image: '/shop/jacket.jpg',\n href: '/shop/jacket',\n },\n {\n title: 'Day Pack',\n tag: 'Bags',\n meta: '$120',\n image: '/shop/pack.jpg',\n href: '/shop/pack',\n },\n // …\n ]}\n />\n );\n}"
"content": "import ArcCarousel from '@/components/tweenui/arc-carousel';\n\nexport default function Page() {\n return (\n <ArcCarousel\n label=\"New this season\"\n items={[\n {\n title: 'Field Jacket',\n tag: 'Outerwear',\n meta: '$240',\n image: '/shop/jacket.jpg',\n href: '/shop/jacket',\n },\n {\n title: 'Day Pack',\n tag: 'Bags',\n meta: '$120',\n image: '/shop/pack.jpg',\n href: '/shop/pack',\n },\n // …\n ]}\n />\n );\n}"
},
{
"path": "examples/arc-carousel-demo-3.tsx",
Expand Down
20 changes: 8 additions & 12 deletions apps/docs/public/r/arc-carousel.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions apps/docs/public/r/registry.json
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@
"name": "svg-draw",
"type": "registry:ui",
"title": "SVG Draw",
"description": "Wrap any inline SVG and it draws itself in: every path, line, circle, rect and polygon traces its outline, one after another, then its fill fades in. Outline icons draw in their own stroke, filled logos are traced and then filled, and the SVG is handed back exactly as written once it settles. Plays when it scrolls into view or on mount; plain CSS transitions, no animation library.",
"description": "Wrap any inline SVG and it draws itself in: every path, line, circle, rect and polygon traces its outline, one after another, then its fill fades in. Outline icons draw in their own stroke, filled logos are traced in a thin line that fades as they fill. Plays when it scrolls into view or on mount. GSAP DrawSVG and ScrollTrigger.",
"files": [
{
"path": "components/tweenui/svg-draw.tsx",
Expand Down Expand Up @@ -1527,7 +1527,7 @@
"name": "arc-carousel",
"type": "registry:block",
"title": "Arc Carousel",
"description": "A showcase carousel whose cards hang on a downward arc, each one further out turned a little more. Stepping swings the whole arc round, the centre card easing up in size under a lime outline, and a card that wraps past the edge keeps travelling off-screen while it comes back in from the far side. Swipe, drag, the arrow keys or a click on a side card all step it.",
"description": "A showcase carousel whose cards hang on a downward arc, each one further out turned a little more. Stepping swings the whole arc round, the centre card easing up in size under a lime outline; cards fade out past the third slot and wrap round unseen. Swipe, drag, the arrow keys or a click on a side card all step it, and it autoplays with a pause button. GSAP with ScrollTrigger.",
"files": [
{
"path": "components/tweenui/arc-carousel.tsx",
Expand Down
Loading
Loading