Book

Design System

Component & Class Reference

app-container

Main content wrapper. Adds responsive horizontal padding.

Live Demo

This div has .app-container — notice the horizontal padding

Usage

<div class="app-container">
  <!-- page content -->
</div>

Expands to: px-5 sm:px-17 lg:px-21.5 xl:px-30.5 pro:px-33.5 2xl:px-37.5

Utility Classes

Non-typography, non-color reusable CSS classes defined in tailwind.css.

.no-scrollbar

Hides the scrollbar on a scrollable element while keeping it scrollable. Works across WebKit and Firefox.

123456789101112
<div class="no-scrollbar overflow-x-auto">...</div>

.bg-curve-size

Responsive background-image sizing helper: no-repeat, switching to bg-cover at xl. Intended for the project's curve/texture background images (see assets/images) — pair it with a bg-[url(...)] utility. Per-breakpoint image swapping isn't wired up yet, so there's no live demo here.

<div class="bg-curve-size bg-[url(...)]">...</div>

Custom Tailwind Variants

Extended Tailwind variants defined in tailwind.css.

tap:

Targets touch devices only (@media (hover: none)). Use for touch-specific styles.

<div class="opacity-0 tap:opacity-100">Touch only</div>

children:

Applies styles to all descendants of the element (& *).

<div class="children:text-green">Every child text is green</div>

active-class:

Targets elements with the .active class (e.g. Vue Router active links).

<NuxtLink class="active-class:font-bold">Nav Link</NuxtLink>

exact-active:

Targets elements with the .router-link-exact-active class, or any element containing one.

<NuxtLink class="exact-active:underline">Nav Link</NuxtLink>

tight:

Targets landscape viewports under 800px height (phones in landscape).

<div class="py-20 tight:py-8">Section</div>

Typography System

Per the Two Moons responsiveness system, the root html font-size scales with viewport width (text-mobile sm:text-ipad-mini lg:text-ipad-pro xl:text-desktop 2xl:text-2xl), and every named class below is expressed in fixed rem sizes relative to that root — never in Tailwind's built-in text-sm/text-lg scale. Resize the window to see these scale proportionally.

Ginger — display

Font: ginger (custom display face). Main heading styles.

display-1

display-2

display-3

display-4

display-5

display-6

<h1 class="display-1">Hero Heading</h1>
<h2 class="display-3">Section Heading</h2>

Ginger — emphasis

Font: ginger (custom display face). Used for <strong> emphasis inside ginger-styled copy.

Plain ginger text with emphasized strong text inside it

<p class="font-ginger text-green-light">
  Plain text with <strong>emphasis</strong>
</p>

Gilda Display — accent

Font: Gilda Display (serif). Uppercase tracked label face for buttons, nav items and eyebrows. Each class bundles uppercase and tracking-accent (0.15em) — override locally where a call site deviates.

accent-1 — 14px fixed

accent-2 — 13px → 14px (sm)

accent-3 — 12px → 13px (sm)

<h1 class="accent-1">Discover</h1>
<button class="accent-2">Book now</button>
<a class="accent-3">Nav item</a>

Manrope — body, button, message

Font: Manrope (sans-serif), self-hosted. The default body font, set on <body> at text-18, dropping to text-16 from sm up. One variable file with a 200–800 wght axis, but the design uses two stops on it: 400 everywhere by default, and font-bold retuned to 600.

text-18 — default body copy on mobile

text-16 — default body copy from sm up

text-button — button / label text

text-message — large message / quote text

<p class="text-18 sm:text-16">Body copy</p>
<button class="text-button">Click me</button>
<p class="text-message">Large quote or message</p>

Colors

The project palette, defined as Tailwind theme colors. Body text defaults to green-dark, swapping to light in dark mode.

light #F9F9F8

off-white #F4F3F2

green-mid #757768

rose #9C5F62

black #000000

white #ffffff

green #5B604A

green-light #848B6F

green-dark #414141

<section class="bg-green text-light">
  <h2>Themed section</h2>
</section>

Dark

Add .dark to any container to declare that the ground under it is dark. It carries the inherited text-light and nothing else — painting is yours, so give the container its own background. It activates the dark: variant on every descendant, which is how headings, form fields and button chips invert without being told individually.

Use .dark-until-lg instead when the ground is dark on a phone and hands back to light from lg — a class can't be toggled per breakpoint, so the variant carries the media query. Everything inside inverts below lg and reverts above it, together. See docs/adr/0014.

Live Demo

Dark section

This block has .dark plus its own bg-green-dark. The body text colour comes along automatically — this paragraph wasn't given one.

This one adds dark:text-green-light, so it only turns green-light while inside a .dark context.

Usage

<!-- Declare a dark ground, and paint it yourself -->
<section class="dark bg-green">
  <h2>Heading</h2>
  <p>Body text is light automatically.</p>

  <!-- Override a single element only in dark context -->
  <p class="dark:text-green-light">Muted line</p>
</section>

<!-- Green on a phone, light from lg -->
<section class="dark-until-lg">…</section>

.dark carries only text-light; .dark-until-lg carries max-lg:text-light. The dark: variant has two arms — it matches &:where(.dark, .dark *) at any width, and &:where(.dark-until-lg, .dark-until-lg *) only below lg.

Breakpoints

Default Tailwind breakpoints, plus one project-specific addition. Each corresponds to a design artboard width — see CONTEXT.md.

PrefixMin width
(default)0px
sm:640px
lg:1024px
pro:1350px (84.375rem) — custom
xl:1280px
2xl:1536px

Reusable Vue Components

Components follow the shadcn philosophy: a single styled primitive whose look is driven by a variant prop, styles composed with cva (button-variants.ts) and merged with the cn() helper (app/lib/utils.ts).

AppButton

A polymorphic button primitive (renders as <button> by default, or anything via as / as-child). It carries the two button styles ported from the legacy ../swasti project. Colour comes from currentColor, so a button inherits its context — hover any of these.

PropTypeDefaultNotes
variant 'line' | 'underline' | 'boxed' | 'hairline' | 'link' 'line'Which ported style to render.
reversebooleanfalselink only — starts drawn, wipes out on hover.
asstring | Component'button'Element/component to render as.
as-childbooleanfalse Merge onto the single child instead of a wrapper (link variant only).
classstringExtra classes, tailwind-merged over the variant.

variant="underline"

The house call-to-action: a solid green-light chip with an uppercase Gilda label — 16px at 0.1em tracking, 20px of padding left and right, 10px top and bottom. Inverts to cream inside a .dark (green) section — see “On a dark surface” below.

<AppButton variant="underline">Contact us</AppButton>

variant="boxed"

A white outline over a 15% white tint rather than a fill, and the one button at 18px rather than 16px. For surfaces that are not .dark-classed but still sit over something dark — the header, whose background changes under it as the page scrolls. Hovering clears the tint.

<AppButton variant="boxed">Book now</AppButton>

variant="line"

The outlined half of the pair: a 10% green-light wash under a label and rule in the same colour at full strength — otherwise the same chip as underline. Hovering fills it in, so the two read as one set. This is the default variant. On a dark ground it swaps green-light for light throughout, keeping every one of those relationships — including a hover that still lands exactly on underline.

<AppButton variant="line">Contact us</AppButton>

variant="hairline"

Uppercase Gilda label (16px, 0.1em) + a hairline that scales on hover while the letter-spacing opens up. The “Discover more ———” treatment. What line was before it became a chip, and the only pre-chip button look still in use: the footer keeps it by choice for its nav and signup button, and AppDiscButton needs it for the label centred inside the disc.

<AppButton variant="hairline">Discover more</AppButton>

variant="link"

A hairline underline that starts drawn and wipes out on hover. Pass :reverse="false" for the opposite: start hidden and wipe in from the right on hover. What underline was before it became a chip; for links that are not buttons — inline hyperlinks in rich text, and the footer's legal row.

<AppButton variant="link">Read our story</AppButton>
<AppButton variant="link" :reverse="false">Read our story</AppButton>

Polymorphic (as / as-child)

Render as a link. Use as for the hairline variant; either as or as-child for link.

<AppButton :as="NuxtLink" to="/" variant="hairline">Home</AppButton>

<AppButton as-child variant="link">
  <NuxtLink to="/">Home</NuxtLink>
</AppButton>

On a dark surface

No dark-mode props are needed — the ground is read from context. hairline and link inherit colour via bg-current / currentColor, so the flag's text-light is all they need. Both chips carry their own dark: inversion, since a green box or a green outline on green would disappear.

The same four inside .dark-until-lg: identical below lg, back to the light-ground treatment above it. Resize to check both.

AppCalendar

A single composite wrapping the reka-ui Calendar primitives — no context file, no per-part wrappers, since Reka's CalendarRoot already provides state to every child. Prev/next page the months, keyboard arrows move the focused day, the selected day fills, today is underlined, and days outside the month dim.

July 2026
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
Event Date, July 2026
<script setup>
import { getLocalTimeZone, today } from '@internationalized/date'

const date = ref(today(getLocalTimeZone()))
</script>

<AppCalendar v-model="date" locale="en" />

AppContentBlock

Prismic bridge stack: AppRichText (renders a rich-text field through PrismicRichText, mapping heading/paragraph/label/hyperlink nodes) over AppNavList (a repeatable group of Link fields, each through the AppLink bridge). CTAs resolve reusable-CTA documents transparently since they flow through the same bridge.

Stay With Us

Nestled in the hills, Swasti offers a quiet escape. Learn more about our rooms.

<AppContentBlock
  :text="slice.primary.body"
  :links="slice.items"
  centered
/>

Contact us for the best offers