# Tailwind for Interaction Hubs **Agentic Coding Best-Practice Guide** *For building governed, observable, antifragile hub interfaces with Tailwind + IHP* **Version:** 0.2 (Agent-Optimized) **Status:** Production-Ready Reference **Audience:** Human developers **and** coding agents (LLMs) **Framework:** IHP (Integrated Haskell Platform) + Interaction Hub Framework (IHF) layer **Date:** March 2026 --- ## 0. Quick-Start for Coding Agents (READ THIS FIRST) When you receive a UI task for an Interaction Hub: 1. **Semantic first** — Never start with classes. Ask: “What IHF primitive/widget does this map to?” 2. **Import hierarchy** — Always `import Web.View.IHF.Primitives`, `Web.View.IHF.Widgets`, `Web.View.IHF.Classes`. 3. **Use wrappers** — Render via `widgetFrame`, `signalBadge`, `hubActionButton`, etc. 4. **Token roles only** — Reference `surfaceBase`, `signalSuccess`, `focusRing` (defined in `Theme.hs`). 5. **No raw soup** — If you see >8 utility classes in one element, extract to a helper. 6. **Document for reproducibility** — Every new primitive gets Haddock + example HSX. This guide is your single source of truth. Follow it → zero drift. --- ## 1. Purpose This guide makes **Tailwind the shared visual grammar** for all Interaction Hubs built on IHP + IHF, while keeping the **IHF semantic model** (Widget, Panel, SignalBadge, etc.) as the architectural control point. It guarantees: - Perfect visual coherence across hubs - Zero CSS entropy - Agent-reproducible styling (no folklore) - Governed evolution via semantic contracts - Full compatibility with IHP’s server-rendered HSX, partial updates, and future islands **Tailwind is the substrate. IHF widgets are the product.** --- ## 2. Core Principle (Agent Rule #1) ```hs -- ALWAYS do this widgetFrame widget content -- NEVER do this
``` **Architecture layers (enforced order):** 1. **IHF Semantic Model** (`Widget`, `HubPanel`, `CommentThread`…) 2. **Visual Primitive Contracts** (`Primitives.hs`) 3. **Tailwind Token Application** (centralized in `Classes.hs` + `tailwind.config.js`) 4. **Screen Composition** --- ## 3. IHP + Tailwind Setup (Mandatory Baseline) Follow the official IHP Tailwind guide, then apply these IHF extensions: ### Required Files (exact locations) ```text Project Root ├── tailwind/ │ ├── tailwind.config.js ← extend with IHF tokens │ └── app.css ├── Web/View/IHF/ │ ├── Theme.hs ← semantic roles │ ├── Tokens.hs ← spacing/radius/shadow maps │ ├── Classes.hs ← reusable class builders │ ├── Primitives.hs ← surface, panel, badge, button… │ ├── Widgets.hs ← widgetFrame, inspectorPanel… │ ├── Patterns.hs ← triageDashboard, decisionBoard… │ ├── AnnotationUI.hs │ └── GovernanceUI.hs ├── Web/View/CustomCSSFramework.hs ← IHP component override (required for JIT) └── Config/Config.hs ← option customTailwind ``` **Key tailwind.config.js snippet (add to IHF theme):** ```js theme: { extend: { colors: { surfaceBase: "hsl(var(--surface-base))", signalSuccess: "hsl(var(--signal-success))", // … all roles from Theme.hs }, spacing: { /* baseline rhythm */ }, } } ``` **CustomCSSFramework.hs** must expose `customTailwind` with Tailwind classes for all IHP form/button/flash helpers. --- ## 4. Recommended Layer Model (Agent Reference) | Layer | Name | Lives In | Example Functions | Purpose | |-------|-----------------------------|-------------------|---------------------------------------|---------| | A | Semantic Interaction | Widgets.hs / Types | `widgetFrame`, `commentRail` | What it *is* | | B | Visual Primitive | Primitives.hs | `panel`, `signalBadge`, `actionButton` | Reusable building blocks | | C | Tailwind Utility Application| Classes.hs | `panelClasses`, `badgeVariant` | Actual classes | | D | Screen Composition | Patterns.hs | `triageDashboard` | Workflow views | --- ## 5. Golden Rules for Agents - **Rule 1**: Every visual element must have a named Haskell wrapper (no raw `
` longer than one line). - **Rule 2**: All color/spacing decisions go through `Theme.hs` roles. - **Rule 3**: Variant logic must be exhaustive and named (`primary | ghost | danger`). - **Rule 4**: Commentability is first-class — every widget primitive exposes `commentable :: Bool`. - **Rule 5**: Before writing any new class string, check `Classes.hs`. If missing, add it there with documentation. --- ## 6. Visual Language & Tokens (Copy-Paste Ready) ### 6.1 Color Roles (Theme.hs) ```hs surfaceBase, surfaceRaised, surfaceMuted, borderSubtle, borderStrong, textPrimary, textSecondary, textMuted, signalInfo, signalSuccess, signalWarning, signalDanger, focusRing, commentAccent, policyAccent, outcomeAccent ``` Map these to CSS variables in `tailwind/app.css` `@layer base`. ### 6.2 Spacing Rhythm (Tokens.hs) ```hs space1, space2, space3, space4, space6, space8, space12, space16 ``` Use only these. Arbitrary values are forbidden. ### 6.3 Radius & Shadow ```hs roundedPanel = "rounded-xl" roundedBadge = "rounded-lg" shadowLight = "shadow-sm" ``` --- ## 7. Core Primitives (Primitives.hs — Starter Template) ```hs -- Example: panel panel :: [Attribute] -> Html -> Html panel attrs content = div! ( [ class_ (panelClasses <> " " <> unwords (map renderAttr attrs)) , role_ "region" ] ) content panelClasses :: Text panelClasses = "bg-surfaceBase border border-borderSubtle rounded-xl shadow-sm px-4 py-3" -- Example: signalBadge signalBadge :: SignalStatus -> Html signalBadge status = span! [ class_ (badgeVariant status) ] [ text (statusLabel status) ] ``` All primitives follow this exact signature pattern. --- ## 8. Widget-Centered Rules (IHF Core) Every `Widget` **must** render via `widgetFrame`: ```hs widgetFrame :: Widget -> (Widget -> Html) -> Html widgetFrame w inner = div! [ class_ widgetFrameClasses , dataAttribute "widget-id" (widgetId w) , dataAttribute "commentable" (show $ isCommentable w) ] $ do widgetHeader w inner w widgetFooter w ``` State cues (inspectable, commentable, in-flight) use **one** consistent visual language (header accent + badge + subtle background shift). --- ## 9. Agent-Friendly Practices ### 9.1 Component Creation Protocol (Mandatory) When creating a new primitive: 1. Add to `Primitives.hs` with full Haddock: ```hs {-# LANGUAGE OverloadedStrings #-} -- | A governed action button. Variants map to IHF policy levels. actionButton :: ActionVariant -> Text -> Html ``` 2. Define variant type in `Types.hs`. 3. Implement class builder in `Classes.hs`. 4. Add example usage in `docs/Examples.hs`. 5. Update `Short Operational Checklist` (section 26). ### 9.2 Prompt Template for Future Agents > “Generate an IHF widget for X using only primitives from Web.View.IHF. Use semantic roles from Theme.hs. No raw Tailwind soup. Include comment affordance.” --- ## 10. Anti-Patterns (Hard-Coded Agent Rejection Rules) - Utility soup longer than 6 classes - Screen-local button/panel/badge reinvention - Literal color names in component signatures - Mixed density without system-level justification - Styling logic inside business controllers - Any custom CSS outside `tailwind/app.css` `@layer components` --- ## 11. Forms, Tables, Annotations, Governance (IHF-Specific) - **Forms**: Use `styledFormGroupClass` from `CustomCSSFramework.hs` + `fieldWithPolicyNote`. - **Tables**: Standardized `sortableTable` in `Patterns.hs` with actor/timestamp/state columns. - **Annotations**: First-class `annotationThread` + triage variants (`open | reviewed | policySensitive`). - **Governance**: `decisionBanner`, `policyScopeBadge`, `traceabilityChain` — all with built-in comment hooks. --- ## 12. File & Module Organization (Exact) See section 3. All IHF UI lives under `Web/View/IHF/`. Import graph is strict: `Patterns.hs` → `Widgets.hs` → `Primitives.hs` → `Classes.hs` → `Theme.hs`. --- ## 13. Rollout Phases & Minimum Standard **Phase 1 (MVP)**: Theme + Tokens + Primitives (panel, button, badge, widgetFrame) **Phase 2**: AnnotationUI + GovernanceUI **Phase 3**: Hub-specific accents + adaptive patterns **Every new component** must ship with: - Purpose - Type signature - Allowed variants - Accessibility notes - Commentability behavior - Example HSX --- ## 14. Short Operational Checklist (Agent + Human Merge Gate) Before any PR/merge: - [ ] Semantic name used - [ ] Imported only from IHF modules - [ ] Classes composed via `Classes.hs` - [ ] State signaling matches existing system - [ ] Focus + contrast + color+icon rule satisfied - [ ] Comment affordance present where applicable - [ ] No custom CSS added - [ ] Agent reproduction test passed (copy-paste into new context works) --- ## 15. Motto **“Use Tailwind for expression. Let IHF widgets define meaning.”** This guide is the contract between humans and agents. Follow it and Interaction Hubs stay coherent, evolvable, and antifragile forever.