Circular Text
TextA ring of text turning slowly around an icon, a logo, or nothing.
The opening of a colour-block site: the headline rises line by line out of its masks, its last word turns through a few in the emphasis face (RotatingText), a round badge with text running around it links further down the page, the lede and two pill actions sit under it, and a strip of tiles in the palette's blocks runs across the bottom (Marquee), each a drawing (Motif) or a picture with a caption. The rise is a CSS animation on load, so it needs no script. Zero JavaScript of its own.
Sample agency
A sample agency that plans, shoots, designs and posts for restaurants, stays and lifestyle brands.
Live, from the library itself, at full size: scroll, hover, navigate away and back. The demo is never gated.
Variants · each row writes the attribute the prop writes; the section's own CSS answers
JavaScript of its own
0 B
CSS 26.8 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.
Browser support
Chrome 105 · Firefox 110 · Safari 16; needs container-units.
The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:
npx shadcn@latest add @moonarc-pro/hero-blocks---
import HeroBlocks from '@/components/moonarc/pro/HeroBlocks.astro';
---
<HeroBlocks
eyebrow="Creative agency"
lines={['A feed that lives', 'up to the']}
words={['real thing.', 'dining room.', 'villa.']}
lede="We plan, shoot, design and post for restaurants, stays and lifestyle brands, and run the ads too."
primary={{ href: '/contact/', label: 'Start a project' }}
secondary={{ href: '/work/', label: 'See the work' }}
badge={{ href: '#work', label: 'See the work', text: 'CREATIVE AGENCY • SINCE 2023 • ' }}
tiles={[
{ label: 'Reel', motif: 'wave', shape: 'tall' },
{ label: 'Menu design', motif: 'plate' },
{ label: 'Live', motif: 'phone', shape: 'tall' },
]}
/>Without a key the registry answers 401 with an upgrade message. Everything this section composes is free: circular-text, magnetic, marquee, motif, rotating-text.
| Prop | Type | Default | Description |
|---|---|---|---|
lines | string[] | none | The headline, one string per line; each line rises from its own mask, a stagger step after the one before. |
words | string[] | none | Words that take turns as the headline's last line, in the emphasis face and colour (--ma-font-em, --ma-em). The first is the one read aloud. |
cycles | number | 3 | Rounds the words turn before the first comes back and stays (WCAG 2.2.2). |
eyebrow | string | none | A short line above the headline. Slot "eyebrow" wins; slot "meta" sits at the other end of the same row (a clock, an opening time). |
lede | string | none | The paragraph under the headline. Slot "lede" wins. |
primary | { href: string; label: string } | none | The first action: a pill in the action colour; on hover the first colour block wells up from below its label. |
secondary | { href: string; label: string } | none | The second action: an outlined pill that fills with the text colour on hover. Slot "actions" replaces both. |
magnetic | boolean | true | The primary action follows the pointer a little (Magnetic). |
badge | { href: string; label: string; text: string } | none | The round badge beside the headline from a 64rem box: where it links, its accessible name, and the text on its ring (CircularText, one turn in 24 s). End the text with a separator so the ring closes. |
tiles | { label?, motif?, block?, shape?, image? }[] | none | The strip under the hero. Each tile paints a colour block (the five in turn when unset) with a drawing, or shows a picture; shape is tall (9 : 16), portrait (4 : 5, default) or square. The strip is decoration and is hidden from assistive technology. |
duration | number | 60 | Seconds the strip takes for one pass. |
level | 1 | 2 | 1 | Heading level: 1 when the hero opens its page, 2 when it is shown under another h1. |
tone | 'surface' | 'muted' | 'ink' | 'brand' | unset (paints nothing) | The ground: the page surface, a muted band, an always-dark ink panel, or a deep brand panel; the actions invert on ink and brand. |
density | 'compact' | 'cozy' | 'roomy' | 'cozy' | Block padding ×0.8 / ×1 / ×1.25 through --ma-density. |
The section reads the theme contract (shadcn's variable names with the base tokens as fallback), so a pasted theme or one --ma-brand recolours it. Its class names are the override surface and stay stable; a named slot replaces the matching prop with your own markup.
.ma-heroblocks.ma-heroblocks__actions.ma-heroblocks__badge.ma-heroblocks__disc.ma-heroblocks__eyebrow.ma-heroblocks__foot.ma-heroblocks__head.ma-heroblocks__inner.ma-heroblocks__lede.ma-heroblocks__line.ma-heroblocks__meta.ma-heroblocks__primary.ma-heroblocks__ring.ma-heroblocks__secondary.ma-heroblocks__strip.ma-heroblocks__tile.ma-heroblocks__title.ma-heroblocks__top.ma-heroblocks__word
slot="eyebrow"slot="meta"slot="lede"slot="actions"slot="aside"
Everything is in place from the start: no rise, the first word stays, the strip and the badge stand still.
ClientRouterCSS animations and the primitives' own bindings; nothing of its own to rebind. Each visit plays the rise again.
Replaces: Agency hero sections (Framer templates). See the migration table.
A ring of text turning slowly around an icon, a logo, or nothing.
Infinite scrolling strip for logos, quotes or tags in any direction.
A drawn picture for a slot that has no photograph yet: a sun over the sea, a split gate, a flower, a grid of tiles, a phone going live, a plate set for dinner, waves under a moon or three shapes.
Cycles through a list of words in place.
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown