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.

August 2026
26
27
28
29
30
31
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
2
3
4
5
Event Date, August 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