Dotted Map
BackgroundsA world map drawn as a grid of dots, with markers that pulse.
Where to find a studio, a shop or a counter, as one card in a colour block: a drawn map with a pin, the address with a directions link, the opening hours for the week, the ways to get in touch, and above them a live line that says whether it is open now and until when (or when it opens next), with the time where the place is. Today's row of the week is marked. The live line is a small script reading the hours in the place's own time zone; without JavaScript the week reads as it is.
Local time:
Visit
| Monday | Closed |
|---|---|
| Tuesday | 10:00–18:00 |
| Wednesday | 10:00–18:00 |
| Thursday | 10:00–18:00 |
| Friday | 10:00–19:00 |
| Saturday | 10:00–17:00 |
| Sunday | Closed |
Closed on public holidays.
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
1006 B raw
626 B gzip.
Uses the shared runtime (1.9 kB raw, once per site). With those included: 2.7 kB raw.
CSS 11.0 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.
Browser support
every browser.
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/visit-card---
import VisitCard from '@/components/moonarc/pro/VisitCard.astro';
---
<VisitCard
address={['Your street 12', 'Your city']}
directions={{ href: 'https://www.openstreetmap.org/', label: 'Directions' }}
timezone="Europe/Lisbon"
hours={[
{ day: 'mon', label: 'Monday' },
{ day: 'tue', label: 'Tuesday', open: '10:00', close: '18:00' },
{ day: 'wed', label: 'Wednesday', open: '10:00', close: '18:00' },
{ day: 'thu', label: 'Thursday', open: '10:00', close: '18:00' },
{ day: 'fri', label: 'Friday', open: '10:00', close: '19:00' },
{ day: 'sat', label: 'Saturday', open: '10:00', close: '17:00' },
{ day: 'sun', label: 'Sunday' },
]}
contacts={[{ label: 'Email', value: 'hello@your-studio.com', href: 'mailto:hello@your-studio.com' }]}
>
<span slot="headline">Come and <em>see the kiln</em></span>
</VisitCard>Without a key the registry answers 401 with an upgrade message.
| Prop | Type | Default | Description |
|---|---|---|---|
address | string[] | none | The address, one line an entry, in an address element. |
hours | { day: 'mon' … 'sun'; label: string; open?: string; close?: string }[] | none | The week; open and close in 24-hour time, both unset for a closed day. |
timezone | string | none | The place's IANA time zone, e.g. Europe/Lisbon: the live line and today's row read the hours in it, wherever the reader is. |
eyebrow | string | none | A short line above the heading. |
headline | string | none | The heading. Slot "headline" wins and may hold an <em>, set in the emphasis face and colour. |
directions | { href: string; label: string } | none | A link to a map. |
contacts | { label: string; value: string; href?: string }[] | none | Ways to get in touch: an email, a phone. |
note | string | none | A line under the week: holidays, appointments. |
openText | string | 'Open now, until {close}' | The live line while open. |
closedText | string | 'Closed now, opens {day} at {open}' | The live line while closed; {day} is todayWord, tomorrowWord or the day's label. |
todayWord | string | 'today' | The word for today in the live line. |
tomorrowWord | string | 'tomorrow' | The word for tomorrow in the live line. |
closedLabel | string | 'Closed' | The word for a closed day in the week. |
hoursLabel | string | 'Opening hours' | The week table's caption, for a screen reader. |
timeLabel | string | 'Local time' | The words a screen reader hears before the time. |
block | 1 | 2 | 3 | 4 | 5 | 2 | The colour block the card paints. |
level | 2 | 3 | 2 | Heading level. |
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-visit.ma-visit__address.ma-visit__card.ma-visit__clock.ma-visit__contact-label.ma-visit__contacts.ma-visit__directions.ma-visit__dot.ma-visit__eyebrow.ma-visit__halo.ma-visit__headline.ma-visit__hole.ma-visit__hours.ma-visit__info.ma-visit__live.ma-visit__map.ma-visit__minor.ma-visit__note.ma-visit__park.ma-visit__pin.ma-visit__roads.ma-visit__water
slot="eyebrow"slot="headline"
The pin's halo does not breathe; the live line and the time still update.
ClientRouterBinds once per swap through the runtime's onMount; the half-minute timer stops when the page is swapped away.
Replaces: Store locator cards (Shopify themes) · Opening hours widgets. See the migration table.
A world map drawn as a grid of dots, with markers that pulse.
Contact · sample page
Sample channels on the left. The form on the right is real and sends nothing here: fill it in and watch the button.
Demo: nothing is sent.
A contact section in two columns: the ways to reach you (email, phone, office) on one side, a form that really sends on the other.
The foot of a maker's site, made like the label sewn into what they make: a panel in a colour block with a stitched edge, the name large (a second part in the emphasis face), the link columns, a row of care symbols drawn as line icons with their words (how to wash it, that it goes in the oven, that it is mended for free) and a few small lines in the mono face.
The next batch, as one colour block: the product drawn large (ProductArt) under a round date stamp, and beside it the heading, a line, how many of the batch are spoken for (a count, and a bar that fills as the panel comes into view), three facts in the mono face and two actions.
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown