README.md

# Glaze Basecoat

[![Package Version](https://img.shields.io/hexpm/v/glaze_basecoat)](https://hex.pm/packages/glaze_basecoat)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/glaze_basecoat/)

This is a collection of Lustre components mapped from [Basecoat UI](https://basecoatui.com/).

Basecoat UI is a Tailwind CSS-based component library inspired by shadcn/ui that works with any web stack.

For a full list of components, take a look at <https://hexdocs.pm/glaze_basecoat> or <https://basecoatui.com/components/>

Latest supported version is [Basecoat v0.3.11](https://github.com/hunvreus/basecoat).

GitHub Pages Demo: <https://daniellionel01.github.io/glaze/glaze_basecoat/>

## Getting Started

```sh
gleam add glaze_basecoat@1
```

```gleam
import glaze/basecoat
import glaze/basecoat/icon
import glaze/basecoat/theme
import glaze/basecoat/theme_switcher
import lustre/element/html

html.head([], [
  // (Optional) Register Tailwind (if not already coming from your build-tool)
  html.script(
    [attribute.src("https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4")],
    "",
  ),
  // Don't forget this, if you're using Tailwind through a CDN!
  theme.tailwind_v4_bridge_style_tag(),
  
  // Register Basecoat Components
  basecoat.register(basecoat.version),
  
  // Register your theme
  theme.style_tag(theme.default_theme()),
  
  // Register Lucide icons, required by some components
  icon.register_cdn("latest"),
  
  // (Optional) Init theme switcher
  theme_switcher.init_script(),
])
```

## Example

In a real project this might look like this:

```gleam
import glaze/basecoat
import glaze/basecoat/button
import glaze/basecoat/card
import glaze/basecoat/theme
import lustre/element/html

pub fn layout() {
  html.html([], [
    html.head([], [
      // ...
      
      theme_switcher.init_script(),
      icon.register_cdn("latest"),
      
      html.script(
        [attribute.src("https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4")],
        "",
      ),
      theme.tailwind_v4_bridge_style_tag(),
      
      basecoat.register(basecoat.version),
      theme.style_tag(theme.default_theme()),
    ]),
    html.body([], [
      card.card([], [
        card.header([], [
          card.title([], [html.text("Welcome")]),
          card.description([], [html.text("Hello!")]),
        ]),
        card.content([], [
          button.button([], [html.text("Get Started")]),
        ]),
      ]),
    ]),
  ])
}
```

You can find the full documentation here: <https://hexdocs.pm/glaze_basecoat>.

Take a look at the [dev module](./dev/glaze_basecoat_dev.gleam) for a kitchen sink of all components and how you might use them!

## Tailwind CSS

Basecoat UI ships Tailwind-based styles. You can either use the CDN for a fast setup, or integrate it into your own Tailwind build.

### TL;DR

| Scenario | CSS | JS | Use these functions |
| --- | --- | --- | --- |
| Use Tailwind CDN | Tailwind CDN + Basecoat CDN | Basecoat JS via CDN | Tailwind `<script ...@tailwindcss/browser@4>`<br>`glaze_basecoat.register(version)`<br>`theme.style_tag(...)`<br>`theme.tailwind_v4_bridge_style_tag()` |
| Build-time Tailwind (CLI/PostCSS/Vite) | Your Tailwind build output (with `@import "basecoat-css"`) | Basecoat JS via CDN | `glaze_basecoat.register_js(version)` <br>`theme.style_tag(...)` |
| Manage CSS/JS separately (still CDN) | Basecoat compiled CSS via CDN | Only specific JS components via CDN | `glaze_basecoat.register_css(version)`<br>`glaze_basecoat.register_component(version, "popover")` |

### Option 1: Full CDN

Use `basecoat.register()` to include Basecoat's compiled CSS and all Basecoat JavaScript via CDN.

### Option 2: Tailwind Play CDN

If you use Tailwind's CDN (<https://tailwindcss.com/docs/installation/play-cdn>), you also need Basecoat's Tailwind v4
`@theme` mapping so utilities like `bg-accent` exist.

If you have a build-time Tailwind setup, you do not need this bridge (it comes from `@import "basecoat-css"`).

```gleam
import glaze/basecoat
import glaze/basecoat/theme
import lustre/attribute.{attribute}
import lustre/element/html

html.head([], [
  html.script(
    [attribute.src("https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4")],
    "",
  ),
  basecoat.register(basecoat.version),
  theme.style_tag(theme.default_theme()),
  theme.tailwind_v4_bridge_style_tag(),
])
```

### Option 3: Build-time Tailwind

If you have a build-time Tailwind setup (Tailwind CLI/PostCSS/Vite/etc) and you install Basecoat from npm, you can import Basecoat directly in your Tailwind entry CSS.

```sh
npm install basecoat-css
```

```css
@import "tailwindcss";
@import "basecoat-css";
```

In this case you should not use `basecoat.register()`, since it also includes Basecoat's CSS from the CDN.
Use `basecoat.register_js()` (or `basecoat.register_component()`) to only include the JavaScript and let your Tailwind build produce the CSS.

```gleam
import glaze/basecoat
import glaze/basecoat/theme
import lustre/attribute.{attribute}
import lustre/element/html

html.head([], [
  basecoat.register_js(basecoat.version),
  theme.style_tag(theme.default_theme()),
])
```

### Option 4: Split CSS and JS (CDN)

If you want to manage CSS and JavaScript separately (still via CDN), register the compiled CSS and only the JS components you use.

```gleam
import glaze/basecoat
import glaze/basecoat/theme
import lustre/element/html

html.head([], [
  basecoat.register_css(basecoat.version),
  basecoat.register_component(basecoat.version, "popover"),
  theme.style_tag(theme.default_theme()),
])
```

## Theming

Basecoat uses shadcn/ui compatible CSS variables. You can customize the theme:

```gleam
import glaze/basecoat/theme

let custom_theme =
  theme.default_theme()
  |> theme.set(theme.Primary, "oklch(0.205 0 0)")
  |> theme.set(theme.Radius, "0.5rem")
```

Browse available themes at [ui.shadcn.com/themes](https://ui.shadcn.com/themes).

You can also use tools like <https://tweakcn.com/editor/theme>!

## Icons

Basecoat uses [Lucide icons](https://lucide.dev).

The icon helpers in `glaze/basecoat/icon` render placeholders like `<i class="lucide" data-lucide="plus">`.
To turn those placeholders into SVGs you must also load the Lucide runtime.

Lucide docs (including the CDN snippet): <https://lucide.dev/guide/packages/lucide>

### Option 1: Bundled

Install `lucide` (npm/pnpm/bun) and use `icon.init()`.

Note: `icon.init()` injects a `<script type="module">` that does `import lucide from "lucide"`.
That means your app must have a JS setup that can resolve the bare module specifier (bundler, import map, etc).

```gleam
import glaze/basecoat/icon
import lustre/element/html

// Initialize Lucide
html.head([], [
  icon.init(),
])

// Use icons
icon.plus([])
icon.search([])
```

### Option 2: CDN

If you are not bundling JavaScript, include Lucide icons via CDN.

```gleam
import glaze/basecoat/icon

html.head([], [
  // Use a pinned version in real projects.
  icon.register_cdn("latest"),
])

// Use icons (same API)
icon.plus([])
icon.search([])
```

## FAQs

### Client vs Server?

This library constructs HTML elements the same way on a client or on a server, so it is compatible in both environments.

## Development

There is a `dev` module that constructs a Demo with all available elements and writes it to a `.html` file:
```sh
gleam dev
open ../docs/glaze_basecoat/index.html
```