Skip to content
Docs
Sign inGet Pro

Nitip Design System (Astro)

Reusable tokens and components for every page of the Nitip template. The published Framer template is the reference for look and behavior; component names follow the Figma file so designers and developers talk about the same thing.

See it live at /style-guide (not linked from the site, not indexed).

src/
design-system/ ← this folder: page-agnostic building blocks
theme.css ← color, breakpoint, easing and animation tokens (Tailwind v4 @theme)
tokens/text.ts ← text styles (h01 … menu)
tokens/motion.ts ← springs and the height reveal
utils/ ← cn(), link helpers, motionStates(), image lookup
scripts/hover.ts ← hover states for data-hover / data-motion (loaded by the layout)
components/ ← actions, brand, navigation, layout, cards, media, data, patterns, forms, cursor, utils
types.ts ← data shapes the components take (ProjectCardData, …)
components/
site/ ← Header, Footer (design system + site content)
sections/ ← page sections, content passed in as props
content/ ← site.json, pages/*.json, collections/*.json, schemas.ts, routes.ts
lib/ ← content.ts (collections), to-ui.ts (content → props), format.ts, forms/
  1. Design-system components never import from src/content. Data comes in through props, so the same component works on any page.
  2. Pages pass content to sections; sections compose design-system components. Keep copy out of components.
  3. Links come from routes (routes.project(slug)) or the content, never hard-coded strings.
  4. Use tokens, not raw values: text.h02, bg-ink, border-line, springButton. If a new value repeats, add a token.
  5. Match Framer first. Any deliberate refinement is documented in the component’s comment.
  6. No framework runtime. Interactivity is plain TypeScript in each component’s <script> (Astro bundles it once per page) using Motion’s animate, hover and inView.
  7. New component? Put it in the right components/ group, add it to /style-guide and the table below.

A hoverable element gets data-hover; every element that changes on hover — the element itself included — gets data-motion with its two states (write the rest state inline too, so the page is right before scripts run):

<a href={href} data-hover data-motion={motionStates({ boxShadow: rest }, { boxShadow: hover })} style={`box-shadow: ${rest}`}>
{label}
<span data-motion={motionStates({ rotate: 0 }, { rotate: 45 })}><Icon name="arrow-up-right" /></span>
</a>

data-transition="soft" picks another spring (default button). Hover is ignored on touch; with reduced motion the change is instant.

Token Values
Breakpoints Phone < 810 · md Tablet 810–1199 · lg Desktop ≥ 1200
Colors ink #101010 · canvas #F6F6F6 · line #D9D9D9 · muted #818181 · accent #FC5000 · star #FDCA49 · danger #FF2244
Font Inter variable (font-sans), loaded by the Astro Fonts API in astro.config.mjs
Text styles text.h01 46/58/72 · h02 44/52/64 · h2 32/36/42 · h3 28/32/36 · h4 24/26/28 · h5 20/22/24 · h6 18/19/20 · hugeParagraph 24/28/32 · largeParagraph 18 · paragraph 16 · smallParagraph 14 · button 14 uppercase · menu 13 uppercase
Section spacing <Section>: sectionGutters 20/30/40, sectionPadding 60/80/100 · sectionGap 60/80/100 between header and content · pageTitlePadding 50 top for page titles (layout/section.ts)
Motion springButton (spring 0.4s, bounce 0.2) · springSoft (0.4s, no bounce) · springSlide (200/40) · springCursor (500/60) · reveal (height reveal) · ease-framer CSS tween for image zooms
Component Figma name Notes
PrimaryButton Primary Button (Default / No Icon) icon={false} for No Icon. Hover: shadow + arrow 45°
SecondaryButton Secondary Button Hover: underline + arrow -45°
BigButton Big Button Footer menu link
SocialMediaLink Social Media Hover: black row, white text
MenuButton Menu (Default / Hover / Active) Active has no hover
TextLink — (Framer link preset) 80% ink + underline on hover; external links open in a new tab
ArrowButton — (Framer slideshow arrow)
SwitchButton Switch Button (Default / Active) Square toggle, #222 outline; Default hover 50% opacity
LoadMoreButton Load More Square ink button, 150 × 40, no hover
Logo Logo (Black / White) tone="light" for dark backgrounds
Icon Phosphor icon set name, tone, size via class
Navigation Navigation (Default / Home / …) Active item from pathname; layout="stack" for mobile
Hamburger Hamburger (Default / Close) The header script opens the menu
Breadcrumbs — (Framer “Breadcrumbs”) Last item is the current page
Section, SectionHeader, SectionLabel, Divider Section / Title Section SectionHeader size="small" for Heading 6 titles; leadClass sets the lead width
PageTitle Page Title Breadcrumbs (optional), h1, divider, description (max 600, descriptionWidth="wide" 850)
ProjectCard Project With Details (Default) “View Project” cursor on the image; titleAs="h2" on index pages
ProjectSimpleCard Project Simple Card (Open / Close) Used by ProjectRecentList
ProjectListItem Project With Details (Desktop / Tablet / Phone List) Row with divider; View Details and image zoom on desktop
PostCard Post Card “Read” cursor
TestimonialCard Testimonial (Desktop / Tablet / Mobile)
TextPoint Text Point List row with dot
HighlightList — (Framer rich text) Bulleted list with a bold lead phrase
FactCard Fact Number tone="dark" for the ink card; ring cursor
TeamCard Team Portrait, name, role, email. No hover
FillImage — Optimized image filling its positioned parent (src = /images/<file>)
CoverImage — (Framer Image + Cover) Bottom gradient with content; ring cursor
ParallaxImage — (Framer Parallax) speed/phoneSpeed/offset/scale; image sized so no gap ever shows; clip={false} to clip in the parent
ScrollTransform — (Framer Scroll Transform, In View) from/phoneFrom (x, y, scale, opacity) → rest while the frame scrolls into view
Chart Chart (Desktop / Desktop Start / Mobile) Grows on scroll; ring cursor
PricingCard Pricing Plan switch (hidden with one plan), included list, price, CTA
Rating, AvatarGroup Hero “User Rating”
AccordionItem Service / Careers row layout="service" | "career"; marker in the marker slot, panel in the default slot
ServiceList Service (Open / Close)
CareerList Careers (Open / Close) Dot marker; pageSize adds a left-aligned Load More
ValueList Value (Desktop / Tablet / Phone) Numbered rows. No hover
ProjectRecentList Project Recent List Hover opens a card
ProjectGallery Projects page Grid / List Switch + ProjectCard grid or ProjectListItem list
PostGrid Blog archive grid 1 / 2 / 3 columns, Load More, 4 or 6 posts per page
RichText — (Framer rich text) Post body: headings 40 above, paragraphs and lists 20; numbered and nested lists, bold leads
Slideshow, TestimonialSlideshow Testimonial Slide Show Autoplay 5s, drag, keyboard
Ticker — (Framer Ticker) 30 px/s, CSS only
BigFooter Big Footer “More Details” cursor
Form — (Framer form) Demo, static endpoint or server route (src/lib/forms/README.md); pending / success / error on the Submit button; resets on success only; honeypot
CheckboxGroup — (Framer checkboxes) Every checked option is sent under one name
FormField — (Framer input / textarea) Label + 40 px input or 200 px message box
SubmitButton Form (Default / Loading / Success / Error) Full width; status for a fixed state
Cursor Cursor (Dot / Active / View Project / Read / More Details) Rendered once in the layout; use data-cursor="…"
CurrentYear — Always-current copyright year

<Cursor /> is rendered once in src/layouts/Layout.astro. Mark elements with data-cursor:

<a href={href} data-cursor="view-project">…</a> <!-- also: "read", "more-details", "ring" -->