# WEB Getting Started

This guide walks through building a simple one-page website in WEB from both a designer and developer perspective.

The goal is not just to show syntax. It is to show:

- what to type
- why each part exists
- which features matter most when you build a real page

By the end, you will have a small landing page with:

- semantic structure
- design tokens
- shared button and card styles
- real HTML attributes
- metadata in `<head>`
- hover states
- responsive behavior
- one small compile-time JavaScript example
- one emitted browser `<script>` tag

## 1. Why WEB Feels Good For One-Page Sites

### Designer perspective

WEB keeps structure and styling close together, so you can think in sections, cards, buttons, spacing, and hierarchy without bouncing across multiple files too early.

That is useful when you are:

- trying different page sections quickly
- tuning rhythm, spacing, and emphasis
- keeping a layout consistent while the content is still moving

### Developer perspective

WEB still compiles to normal HTML and CSS, but it gives you a cleaner authoring model for:

- semantic tags through type declarations
- reusable patterns through shared selectors and style inheritance
- real HTML attributes through `::attrs`
- metadata through `::head`
- browser scripts through `::script`
- responsive rules through `::media`

In other words, you get a single source file for authoring, but the output is still web-native.

## 2. What We Are Building

We will make a simple studio-style landing page with:

1. a top navigation
2. a hero section
3. a three-card feature section
4. a final call-to-action section
5. a footer note

If you want a starter project generated for you first, run:

```bash
web init
```

or, if you want to be explicit about the current directory:

```bash
web init .
```

or, if you want WEB to create a new project folder for you:

```bash
web init website
```

That creates:

- `index.web`
- `index.html`
- `index.css`
- `web-lang-agents.md`

The generated `web-lang-agents.md` combines the packaged getting-started guide, CLI guide, and compiler reference into one file so local agents can load the current WEB context quickly.

If you want to follow along exactly, create a file called:

```bash
studio.web
```

## 3. Compile It

Once your file exists, compile it with the CLI:

```bash
web studio.web
```

or:

```bash
web studio
```

That will generate:

- `studio.html`
- `studio.css`

Capture a screenshot of the compiled page:

```bash
web screenshot studio.web
```

Keep the file rebuilding while you work:

```bash
web watch studio.web
```

Keep a watch session running with baseline and timed screenshots:

```bash
web watch studio.web -s
```

## 4. A Complete Working Example

Copy this into `studio.web`:

```web
define {
  // Shared design tokens.
  @surface = "#0f172a";
  @surfaceSoft = "rgba(255, 255, 255, 0.08)";
  @textStrong = "#f8fafc";
  @textMuted = "#cbd5e1";
  @accent = "#67e8f9";
  @accentStrong = "#22d3ee";
  @cardBorder = "1px solid rgba(255, 255, 255, 0.12)";
  @shadow = "0 24px 80px rgba(2, 8, 23, 0.35)";
  @radiusLarge = "28px";
  @radiusPill = "999px";
  @sectionGap = "clamp(3rem, 8vw, 6rem)";

  // Semantic page structure.
  Main pageMain;
  Nav siteNav;
  Link brandLink;
  Link workLink;
  Link contactLink;

  // A reusable button pattern.
  Link buttonBase;
  buttonBase primaryLink;
  buttonBase secondaryLink;
  primaryLink extends buttonBase;
  secondaryLink extends buttonBase;

  // Hero content.
  Section heroSection;
  Span heroEyebrow;
  Heading1 heroTitle;
  Paragraph heroCopy;

  // Feature content.
  Section featureSection;
  Span featureEyebrow;
  Heading2 featureTitle;
  Paragraph featureCopy;
  Article featureCard;
  featureCard clarityCard;
  featureCard speedCard;
  featureCard handoffCard;
  clarityCard extends featureCard;
  speedCard extends featureCard;
  handoffCard extends featureCard;

  // Call to action.
  Section ctaSection;
  Heading2 ctaTitle;
  Paragraph ctaCopy;

  // Footer.
  Footer pageFooter;
  Paragraph footerNote;
}

// Document metadata.
::head {
  meta {
    name = "description";
    content = "A simple one-page portfolio layout built in WEB.";
  }

  meta {
    name = "theme-color";
    content = "#0f172a";
  }
}

// Browser-side JavaScript emitted near the end of <body>.
::script {
  code {
    document.documentElement.dataset.webReady = "true";
  }
}

// Global CSS.
* {
  boxSizing = "border-box";
}

html {
  background = "radial-gradient(circle at top, #12213f 0%, #0f172a 55%, #020617 100%)";
  color = @textStrong;
  fontFamily = "Inter, \"Segoe UI\", sans-serif";
  lineHeight = 1.5;
  scrollBehavior = "smooth";
}

// Shared button styles. Derived links inherit these.
buttonBase {
  display = "inline-flex";
  alignItems = "center";
  justifyContent = "center";
  padding = "0.95rem 1.25rem";
  border = @cardBorder;
  borderRadius = @radiusPill;
  textDecoration = "none";
  fontWeight = 700;
  transition = "transform 0.2s ease, background 0.2s ease, color 0.2s ease";
  raw = `
    &:focus-visible {
      outline: 3px solid rgba(255, 255, 255, 0.72);
      outline-offset: 4px;
    }
  `;
}

primaryLink {
  background = @accent;
  color = "#06202a";

  ::hover {
    background = @accentStrong;
    transform = "translateY(-2px)";
  }
}

secondaryLink {
  background = "transparent";
  color = @textStrong;

  ::hover {
    background = @surfaceSoft;
    transform = "translateY(-2px)";
  }
}

// Shared card states.
featureCard {
  transition = "transform 0.2s ease, border-color 0.2s ease";

  ::hover {
    transform = "translateY(-4px)";
    borderColor = "rgba(103, 232, 249, 0.35)";
  }
}

pageMain {
  width = "min(100% - 2rem, 1120px)";
  margin = "0 auto";
  padding = "1.5rem 0 4rem";
  display = "grid";
  gap = @sectionGap;

  siteNav {
    display = "flex";
    alignItems = "center";
    gap = "1rem";
    padding = "1rem 1.25rem";
    background = @surfaceSoft;
    border = @cardBorder;
    borderRadius = @radiusLarge;
    boxShadow = @shadow;

    brandLink {
      textContent = "Northstar Studio";
      fontWeight = 800;
      letterSpacing = "0.04em";

      ::attrs {
        href = "#top";
        ariaLabel = "Back to the top of the page";
      }
    }

    workLink {
      textContent = "Process";
      marginLeft = "auto";
      color = @textMuted;
      textDecoration = "none";

      ::attrs {
        href = "#features";
      }
    }

    contactLink {
      textContent = "Contact";
      color = @textMuted;
      textDecoration = "none";

      ::attrs {
        href = "#cta";
      }
    }
  }

  heroSection {
    display = "grid";
    gap = "1.25rem";
    padding = "clamp(2rem, 6vw, 4.5rem)";
    background = "linear-gradient(180deg, rgba(15, 23, 42, 0.92) 0%, rgba(15, 23, 42, 0.72) 100%)";
    border = @cardBorder;
    borderRadius = @radiusLarge;
    boxShadow = @shadow;

    ::attrs {
      id = "top";
    }

    heroEyebrow {
      textContent = "Designer-friendly. Developer-ready.";
      color = @accent;
      fontWeight = 700;
      letterSpacing = "0.08em";
      textTransform = "uppercase";
      fontSize = "0.85rem";
    }

    heroTitle {
      textContent = "Build a polished one-page website from one WEB file.";
      margin = "0";
      maxWidth = "12ch";
      fontSize = "clamp(2.8rem, 7vw, 5rem)";
      lineHeight = 1;
    }

    heroCopy {
      innerHTML = "Use semantic structure, shared styles, metadata, responsive rules, and compile-time helpers without splitting your thinking across many files.";
      margin = "0";
      maxWidth = "62ch";
      color = @textMuted;
      fontSize = "1.05rem";
    }

    // Names like heroActions and featureGrid are handy layout wrappers.
    // If a name does not map to a special built-in tag, WEB falls back to <div>.
    heroActions {
      display = "flex";
      flexWrap = "wrap";
      gap = "0.75rem";
      marginTop = "0.5rem";

      primaryLink {
        textContent = "See the walkthrough";

        ::attrs {
          href = "#features";
          ariaLabel = "Jump to the feature walkthrough section";
        }
      }

      secondaryLink {
        textContent = "Start a project";

        ::attrs {
          href = "#cta";
          ariaLabel = "Jump to the contact section";
        }
      }
    }
  }

  featureSection {
    display = "grid";
    gap = "1rem";

    ::attrs {
      id = "features";
    }

    featureEyebrow {
      textContent = "Why this layout works";
      color = @accent;
      fontWeight = 700;
      letterSpacing = "0.08em";
      textTransform = "uppercase";
      fontSize = "0.85rem";
    }

    featureTitle {
      textContent = "A clean one-page structure that supports both craft and handoff.";
      margin = "0";
      maxWidth = "16ch";
      fontSize = "clamp(2rem, 5vw, 3.5rem)";
      lineHeight = 1.05;
    }

    featureCopy {
      textContent = "The sections below show the core ideas you will use most often in WEB: shared tokens, semantic structure, HTML attributes, hover states, responsive rules, and small compile-time touches.";
      margin = "0";
      maxWidth = "70ch";
      color = @textMuted;
    }

    featureGrid {
      display = "grid";
      gridTemplateColumns = "repeat(3, minmax(0, 1fr))";
      gap = "1rem";
      marginTop = "0.75rem";

      clarityCard {
        innerHTML = "<h3>One file, less context switching</h3><p>Structure and styling stay close together, which makes early design exploration faster and calmer.</p>";
      }

      speedCard {
        innerHTML = "<h3>Reusable patterns without a framework</h3><p>Base selectors and style inheritance let you keep buttons and cards consistent without repeating yourself.</p>";
      }

      handoffCard {
        innerHTML = "<h3>Better developer handoff</h3><p>Metadata, attributes, responsive rules, and browser script output all stay explicit and easy to review.</p>";
      }
    }
  }

  ctaSection {
    padding = "clamp(2rem, 5vw, 3rem)";
    background = @surfaceSoft;
    border = @cardBorder;
    borderRadius = @radiusLarge;
    boxShadow = @shadow;
    display = "grid";
    gap = "1rem";

    ::attrs {
      id = "cta";
    }

    ctaTitle {
      textContent = "Ready to adapt this into your own landing page?";
      margin = "0";
      fontSize = "clamp(1.8rem, 4vw, 2.8rem)";
    }

    ctaCopy {
      textContent = "Swap the copy, colors, and sections first. Once the layout feels right, add more states, animations, or generated content as needed.";
      margin = "0";
      maxWidth = "58ch";
      color = @textMuted;
    }

    primaryLink {
      textContent = "Book a kickoff call";

      ::attrs {
        href = "mailto:hello@northstar.studio";
        ariaLabel = "Email Northstar Studio to start a project";
      }
    }
  }

  pageFooter {
    paddingTop = "0.5rem";
    borderTop = @cardBorder;

    footerNote {
      textContent = js`
        const year = new Date().getFullYear();
        return "Prototype generated in " + year + " with WEB.";
      `;
      margin = "0";
      color = @textMuted;
    }
  }
}

// CSS-only styling for cards inside the grid.
featureGrid {
  styles {
    featureCard {
      padding = "1.5rem";
      background = @surfaceSoft;
      border = @cardBorder;
      borderRadius = "22px";
      minHeight = "220px";
      boxShadow = @shadow;
    }
  }
}

// Responsive behavior.
::media (max-width: 820px) {
  pageMain {
    width = "min(100% - 1.25rem, 1120px)";
  }

  pageMain.siteNav {
    flexWrap = "wrap";
  }

  pageMain.heroSection {
    padding = "1.5rem";
  }

  pageMain.heroSection.heroActions {
    flexDirection = "column";
    alignItems = "stretch";
  }

  pageMain.featureSection.featureGrid {
    gridTemplateColumns = "1fr";
  }

  buttonBase {
    width = "100%";
  }
}
```

This example was checked against the current compiler, so it is meant to be copy-pasteable.

## 5. How The Example Is Structured

### `define { ... }` gives you vocabulary

Why it matters:

- designers get a simple place for tokens like colors, radius, and spacing
- developers get semantic names that resolve to real HTML tags
- both sides get a shared vocabulary for the page

The `define` block does three important jobs:

1. stores reusable values like `@accent` and `@shadow`
2. declares semantic objects like `Main pageMain;` and `Section heroSection;`
3. creates shared style families like `buttonBase`, `primaryLink`, and `secondaryLink`

If you remember one thing, remember this: `define` is where you name the system before you build the page.

### `::head` handles page metadata

Why it matters:

- designers care about how the page presents itself in tabs, previews, and browser chrome
- developers need a clean place for metadata that belongs in `<head>`

In this example, `::head` adds:

- a description
- a theme color

Use `::head` when information belongs to the document itself, not to a specific section of the page.

### Global blocks set the canvas

Why it matters:

- `* { ... }` is useful for universal layout corrections like `box-sizing`
- `html { ... }` is your whole-page canvas for background, type, and general feel

This is where the page starts to feel designed instead of just structured.

### Normal blocks create the page structure

Why it matters:

- this is the core WEB authoring model
- your page structure and much of your styling are expressed together

Inside `pageMain`, we build:

- `siteNav`
- `heroSection`
- `featureSection`
- `ctaSection`
- `pageFooter`

That keeps the page easy to scan because the source file mirrors the visual hierarchy.

### `::attrs` adds real HTML behavior

Why it matters:

- designers often need anchor jumps, labels, and IDs for navigation
- developers need real HTML attributes, not just visual styling

We use `::attrs` for:

- `href`
- `ariaLabel`
- `id`

This is the bridge between a styled layout and an actually usable webpage.

### Shared styles reduce repetition

Why it matters:

- design systems live or die on consistency
- repeated button and card rules are a maintenance trap

The example uses:

- `buttonBase` for shared button-like link styles
- `primaryLink extends buttonBase;`
- `secondaryLink extends buttonBase;`
- `featureCard` as a shared card family

That means you style the pattern once, then specialize the variants.

### `styles { ... }` is great for selector-only styling

Why it matters:

- sometimes you want styling that depends on where something lives
- you may not want to attach every shared style directly to the node declaration itself

In the example:

```web
featureGrid {
  styles {
    featureCard {
      ...
    }
  }
}
```

This says: “when a `featureCard` appears inside `featureGrid`, style it like this.”

That is useful when a pattern changes by context.

### `::hover` adds state without leaving WEB

Why it matters:

- interactive polish matters even on a small one-page layout
- hover states help communicate hierarchy and clickability

We use `::hover` for:

- button lift and color changes
- card lift and border emphasis

This gives the page enough interaction to feel intentional without adding heavy complexity.

### `raw` is your escape hatch

Why it matters:

- sometimes the cleanest WEB syntax is not enough for one detail
- you still need a safe way to express real CSS when necessary

The example uses `raw` for:

- `:focus-visible` styling on the shared button base

That is a good example of when `raw` is appropriate: a small, targeted enhancement that would be awkward to express otherwise.

### `::media` keeps the layout responsive

Why it matters:

- a one-page site has to work on phones, not just desktops
- responsive rules should be easy to find

In the example, the media block:

- wraps the nav if needed
- reduces hero padding
- stacks the hero actions
- collapses the feature grid to one column
- makes button patterns full-width on smaller screens

This is the moment where the page becomes production-minded rather than just visually nice.

### Compile-Time `js` Literals Help With Small Generated Values

Why it matters:

- sometimes you want a tiny generated value without introducing browser runtime logic

The footer note uses:

```web
footerNote.textContent = js`
  const year = new Date().getFullYear();
  return "Prototype generated in " + year + " with WEB.";
`;
```

That runs at compile time, not in the browser.

Use this for:

- small generated strings
- simple content loops
- compile-time fragments

Do not use it when you actually need runtime browser behavior.

### `::script` is for emitted browser JavaScript

Why it matters:

- compile-time JavaScript and browser JavaScript are different concerns

In this example, `::script` adds a small browser-side enhancement:

```web
::script {
  code {
    document.documentElement.dataset.webReady = "true";
  }
}
```

That script ends up in the generated page output.

Use `::script` when the browser should run the code after the page loads.

## 6. What To Change First

If you are a designer, start by changing:

- the copy
- the accent color
- the background treatment
- card spacing and radius
- section order

If you are a developer, start by changing:

- the `::head` metadata
- `href` and `ariaLabel` values
- the responsive breakpoint in `::media`
- the inline script in `::script`
- the reusable base selectors like `buttonBase` and `featureCard`

## 7. A Good Mental Model For Editing

When you are changing a WEB file, ask:

1. Is this document metadata? Then use `::head`.
2. Is this a real HTML attribute? Then use `::attrs`.
3. Is this page structure or content? Then use a normal block.
4. Is this CSS-only styling by selector context? Then use `styles { ... }`.
5. Is this a state like hover? Then use `::pseudo`.
6. Is this responsive behavior? Then use `::media`.
7. Is this an unusual CSS detail? Then use `raw`.
8. Is this generated at compile time? Then use a `js` literal.
9. Is this browser runtime logic? Then use `::script`.

That single decision tree will keep most projects clean.

## 8. Common Beginner Mistakes

These are the most common first-run issues:

- forgetting `;` at the end of a statement
- placing `define { ... }` after normal page rules
- putting CSS properties inside `::attrs`
- nesting `::head` or `::script` inside a page section
- forgetting quotes around values like `#0f172a` or `/docs`
- expecting a `js` literal to run in the browser

If something goes wrong, read:

- `./error-handling.md` for troubleshooting patterns
- `./language-guide.md` for language rules
- `./cli.md` for CLI usage

For day-to-day workflow, a good rhythm is:

1. use `web init` when you want a ready-made starter page plus a bundled `web-lang-agents.md` context file
2. use `web studio.web` when you want a one-off compile
3. use `web screenshot studio.web` when you want a visual artifact to review or share
4. use `web watch studio.web -s` when you want a live working session with periodic visual snapshots

## 9. Where To Go Next

After this walkthrough, a natural next step is:

1. duplicate the example into a second `.web` file
2. replace the copy and tokens to match your own brand
3. add another section such as testimonials, pricing, or a contact form
4. introduce a second responsive breakpoint
5. add one carefully chosen `raw` enhancement only if normal WEB syntax stops being enough

That is usually the fastest path from “I understand the language” to “I can ship with it.”
