Apple Hello Effect
A decorative SVG that 'writes' the word hello in flowing cursive, drawn stroke-by-stroke via an animated stroke-dashoffset. Pure CSS, motion-gated, token-coloured.
Preview
<div class="flex min-h-40 w-full items-center justify-center rounded-lg border border-border bg-card p-8">
<x-ui.apple-hello-effect class="h-28 w-auto text-foreground" :speed="2.5" loop />
</div>
Installation
php artisan ui:add apple-hello-effect
Registry contract
php artisan ui:add apple-hello-effect
writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.
-
resources/views/components/ui/apple-hello-effect.blade.php
- Registry dependencies
- None — installs on its own.
- Packages
-
composer: jml/brok:^0.2
Use with AI
A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.
# Brok UI: Apple Hello Effect (`apple-hello-effect`)
A decorative SVG that 'writes' the word hello in flowing cursive, drawn stroke-by-stroke via an animated stroke-dashoffset. Pure CSS, motion-gated, token-coloured.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add apple-hello-effect
```
## Usage
```blade
<div class="flex min-h-40 w-full items-center justify-center rounded-lg border border-border bg-card p-8">
<x-ui.apple-hello-effect class="h-28 w-auto text-foreground" :speed="2.5" loop />
</div>
```
## Props
- `speed` (float, default `2.5`) — Seconds for one full draw of the word.
- `loop` (bool, default `false`) — Repeats the draw animation forever instead of drawing once and stopping.
- `label` (mixed|null, default `null`) — Accessible name for the figure; defaults to the localized word "hello".
## Use when
- Use only when visual expression supports storytelling, brand differentiation, or a clear showcase moment.
- Adding a decorative, one-time or looping cursive "hello" write-on animation to a hero, splash, or empty state.
- Wanting a brand-flavoured entrance flourish that is pure CSS and themes with the foreground token.
## Avoid when
- Do not place heavy effects in task-dense workflows where clarity, speed, and readability matter more.
- Needing arbitrary text drawn stroke-by-stroke; this component's SVG path always spells "hello", only the accessible label is configurable.
- Needing a functional loading indicator; use a loader component instead, since this is purely decorative.
## Anti-patterns
- Heavy effects in forms or task-dense workflows
- Motion that interferes with reading or focus
## Rules
- Use the `<brok:apple-hello-effect>` tag (or `<x-ui.apple-hello-effect>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.
## Links
- Docs: https://brokui.dev/docs/components/apple-hello-effect
- Registry JSON (files, props, contract): https://brokui.dev/r/open/apple-hello-effect.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| speed | float | 2.5 | Seconds for one full draw of the word. |
| loop | bool | false | Repeats the draw animation forever instead of drawing once and stopping. |
| label | mixed | null | null | Accessible name for the figure; defaults to the localized word "hello". |
Slots
default— Unused; the component has no visible default slot content.
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- The word draws itself once via an animated stroke-dashoffset on a single continuous path; loop repeats the draw indefinitely.
- Reduced motion shows the word fully drawn and static instead of animating.
- Declares registry capability flags: a11y, responsive, rtl, darkMode, localized.
Guidance
Support brand expression or a showcase moment.
Use when
- Use only when visual expression supports storytelling, brand differentiation, or a clear showcase moment.
- Adding a decorative, one-time or looping cursive "hello" write-on animation to a hero, splash, or empty state.
- Wanting a brand-flavoured entrance flourish that is pure CSS and themes with the foreground token.
Avoid when
- Do not place heavy effects in task-dense workflows where clarity, speed, and readability matter more.
- Needing arbitrary text drawn stroke-by-stroke; this component's SVG path always spells "hello", only the accessible label is configurable.
- Needing a functional loading indicator; use a loader component instead, since this is purely decorative.
Use instead
- Static content-first presentation
Anti-patterns
- Heavy effects in forms or task-dense workflows
- Motion that interferes with reading or focus
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- No component-owned keyboard interaction; native element behavior applies.
- Focus
none
- The root is an SVG with role="img" and an aria-label built from the label prop, so screen readers get the word as text even though it is drawn as a path.
- The animation only changes stroke-dashoffset, never colour, so the figure stays legible under forced-colour and high-contrast modes.
- Semantic HTML and a stable
data-slotattribute for styling and scripting hooks. - Focus-visible rings use the
ringtoken, so keyboard focus is always visible. - Disabled and invalid states are conveyed to assistive tech, not by color alone.
- Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
- Labels go through
__()and layout uses logical properties (ms-*,text-start), so it mirrors underdir="rtl"— flip the preview to RTL to confirm. - Dark mode uses the same semantic tokens under the
darkclass; high contrast follows forced-color system tokens.
Livewire
Livewire can update this component through forwarded wire:* attributes.
Source
The exact, editable file ui:add writes
into your app. Previews render this same code; there are no preview-only components.
@props([
// Seconds for one full draw of the word.
'speed' => 2.5,
// Repeat the draw forever.
'loop' => false,
// Accessible name for the figure (also the word it spells).
'label' => null,
])
@php
// Apple's iconic "hello" — a single connected cursive stroke "written" by
// animating stroke-dashoffset from a large value down to 0, so the line
// appears to draw itself. The motion lives in the scoped <style> below and
// touches ONLY stroke-dashoffset (never colour), so the design tokens stay
// the single source of truth. Pure CSS, no JS; reduced-motion users get the
// word fully drawn and static.
//
// The path is ONE flowing lowercase script — an entry sweep, a looped `h`,
// a small `e`, two looped `l`s and the `o` with an exit flourish — so the
// dash draws it as a single unbroken line, the way the original is written.
$label = $label ?? __('hello');
// Standard inline timing only — duration is a real CSS property, NOT spliced
// into a Tailwind utility class (the JIT could not see that). The fixed
// `dasharray`/`dashoffset` and the keyframe handle the rest.
$duration = (float) $speed;
@endphp
{{-- Keyframes carry NO colour; reduced-motion users get the finished word. --}}
<style>
@keyframes ui-hello-draw {
from { stroke-dashoffset: 1000; }
to { stroke-dashoffset: 0; }
}
[data-slot="apple-hello-effect"] .ui-hello-path {
/* The path declares pathLength="1000", so the dash maths is independent
of the path's real length — the whole word always draws over `speed`. */
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
}
@media (prefers-reduced-motion: no-preference) {
[data-slot="apple-hello-effect"] .ui-hello-path {
animation-name: ui-hello-draw;
animation-timing-function: cubic-bezier(0.45, 0, 0.3, 1);
animation-fill-mode: forwards;
animation-iteration-count: 1;
}
[data-slot="apple-hello-effect"][data-loop="true"] .ui-hello-path {
animation-iteration-count: infinite;
}
}
@media (prefers-reduced-motion: reduce) {
[data-slot="apple-hello-effect"] .ui-hello-path {
stroke-dashoffset: 0;
animation: none;
}
}
</style>
<svg
data-slot="apple-hello-effect"
data-loop="{{ $loop ? 'true' : 'false' }}"
role="img"
aria-label="{{ $label }}"
viewBox="0 0 400 160"
fill="none"
stroke="currentColor"
stroke-width="9"
stroke-linecap="round"
stroke-linejoin="round"
{{ $attributes->merge(['class' => 'text-foreground h-16 w-auto max-w-full']) }}
>
{{-- One continuous stroke: entry sweep, the looped h, a small e, two looped
l's and the o with its exit flourish — so the dash draws it as one line. --}}
<path
class="ui-hello-path"
pathLength="1000"
style="animation-duration: {{ $duration }}s"
d="M14 126 C40 112 66 98 78 64 C86 38 92 22 100 24 C108 28 100 58 88 90 C82 106 74 120 68 126 C66 130 70 132 74 126 C84 104 100 86 114 88 C126 90 126 108 122 120 C120 128 126 132 132 126 C140 116 150 102 162 100 C176 98 178 112 166 114 C154 116 146 122 150 130 C156 138 174 128 184 112 C192 96 204 60 212 36 C216 22 228 16 232 28 C234 42 222 72 210 102 C204 116 202 128 210 130 C218 132 228 120 234 108 C242 92 254 58 262 36 C266 22 278 16 282 28 C284 42 272 72 260 102 C254 116 252 128 260 130 C268 132 278 118 284 108 C292 96 306 90 316 94 C330 100 332 120 320 128 C308 134 294 128 294 116 C294 104 306 94 318 94 C328 94 336 102 342 110 C348 118 356 116 366 106"
/>
</svg>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.1.2 - Status
- Stable
- License
-
open - Accessibility reviewed
- No review date recorded
- Last breaking change
- No date recorded
- Deprecation
- Not deprecated
- Contract
-
v6 - Foundation
-
≥ 1.0.0