# Assets and Styling

Iwf vendors the framework assets used by the default shell and exposes helpers
for app assets. Basecoat is the default styling target, while unstyled rendering
is available when an app owns all markup and CSS.

## 1. Framework Assets

`runApp` mounts default framework assets under:

```text
/__iwf/assets
```

The default shell uses fingerprinted versions of:

- htmx
- morphdom-swap support
- Basecoat CSS/JS
- Iwf client script

Generated apps do not rely on production CDNs for the default shell.

## 2. App Static Assets

Use embedded static assets:

```idris
appCss : StaticAsset
appCss =
  stylesheet "app.css" "body { font-family: sans-serif; }"

appWithAssetRoutes : Routes
appWithAssetRoutes =
  staticRoutes "/assets" [appCss] ++ appRoutes
```

Plain asset paths use revalidation cache headers and `ETag`.

Use fingerprinted assets for immutable caching:

```idris
cssHref : String
cssHref =
  fingerprintedAssetHref "/assets" appCss

assetRoutes : Routes
assetRoutes =
  fingerprintedStaticRoutes "/assets" [appCss]
```

## 3. Development Refresh

Add asset refresh during local development:

```idris
devLayout : PageLayout
devLayout context page =
  { pageHead := page.pageHead ++ [devAssetRefreshScript] } page

devPage : Html
devPage =
  fragment
    [ <title>Development</title>
    , <main id="main-content">
        <h1>Development</h1>
      </main>
    ]
```

The browser polls the asset version endpoint and reloads when the served asset
bundle changes.

For browser reloads during local work, use:

- `devLiveReloadScript`
- `devLiveReloadRoutes`

## 4. Basecoat

Basecoat-oriented defaults are used by:

- generated direct HTML starter
- form helpers
- submit buttons
- flash messages
- pagination
- breadcrumbs
- job dashboard

Use the app build to run Basecoat IDRX checking:

```sh
iwf build
```

For fully custom styling, return direct page HTML with `render` and configure
shared shell attributes once with `withPageLayout`:

- `unstyledFormInput`
- `unstyledFormErrors`
- `unstyledSubmitButton`

## 5. Tailwind and NPM

Iwf does not provide a first-class NPM/Yarn workflow. The canonical example has
a Tailwind watcher because that app chooses to use it, not because the framework
requires it. App-specific frontend tooling can live beside Iwf without becoming
part of the framework contract.

## Next

Read [Navigation, Pagination, and SEO](navigation-pagination-and-seo.md).
