Skip to content
Brok UI

Loading…

No results

Equation

Open source

Renders a LaTeX mathematical expression via KaTeX, with display (block) and inline modes, a token-styled container, and a raw-expression fallback for no-JS environments.

Version
v1.0.4
Stability
stable
License
MIT
Related
Citation
Kbd
Code Line

Preview

e^{i\pi} + 1 = 0

Inline math like a^2 + b^2 = c^2 flows with the text.

previews.components.equation.default.blade.php Blade
<div class="flex flex-col items-center gap-4 p-6">
    <x-ui.equation expression="e^{i\pi} + 1 = 0" />
    <p class="text-sm text-muted-foreground">
        {{ __('Inline math like') }} <x-ui.equation :inline="true" expression="a^2 + b^2 = c^2" /> {{ __('flows with the text.') }}
    </p>
</div>

Installation

terminal
php artisan ui:add equation

Note

This component ships an Alpine behavior module at resources/js/ui/equation.js. Import it once from your bundle so it registers on alpine:init:

resources/js/ui/index.js JS
import './equation.js';

Registry contract

php artisan ui:add equation writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.

  • blade resources/views/components/ui/equation.blade.php
  • js resources/js/ui/equation.js
Registry dependencies
None — installs on its own.
Packages
composer: jml/brok:^0.2
npm: katex
npm: alpinejs

Use with AI

A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.

equation.md
# Brok UI: Equation (`equation`)

Renders a LaTeX mathematical expression via KaTeX, with display (block) and inline modes, a token-styled container, and a raw-expression fallback for no-JS environments.

Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.

## Install

```bash
php artisan ui:add equation
```

## Usage

```blade
<div class="flex flex-col items-center gap-4 p-6">
    <x-ui.equation expression="e^{i\pi} + 1 = 0" />
    <p class="text-sm text-muted-foreground">
        {{ __('Inline math like') }} <x-ui.equation :inline="true" expression="a^2 + b^2 = c^2" /> {{ __('flows with the text.') }}
    </p>
</div>
```

## Props

- `expression` (string, default `E = mc^2`) — LaTeX expression to render.
- `inline` (bool, default `false`) — Renders inline and baseline-aligned instead of as a centered display block.

## Use when

- Use to summarize, sequence, or present data so users can scan it quickly.
- Rendering a LaTeX mathematical expression as typeset math, inline or as a centered block.

## Avoid when

- Do not add display-only ornament when the user needs actionable structure or exact comparison instead.
- The content is plain text with no mathematical notation; render it as normal text instead. Citing a source rather than typesetting math; use citation instead.

## Anti-patterns

- Adding display ornament without informational value

## Rules

- Use the `<brok:equation>` tag (or `<x-ui.equation>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.

## Links

- Docs: https://brokui.dev/docs/components/equation
- Registry JSON (files, props, contract): https://brokui.dev/r/open/equation.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

API

manifest knowledge + registry-derived coverage

Props

Props accepted by this component: name, type, default value and description.
Prop Type Default Description
expression string E = mc^2 LaTeX expression to render.
inline bool false Renders inline and baseline-aligned instead of as a centered display block.

Slots

  • default —

Data slots

Stable hooks for CSS overrides and browser tests.

equation

Behavior

  • Renders the raw expression as text first for the no-JS/SSR fallback, then Alpine's init() replaces it with KaTeX-rendered HTML.
  • inline switches between an inline-flex baseline-aligned span and a centered block with vertical margin.
  • Installs a JavaScript behavior module when the registry item includes resources/js/ui files.
  • Declares registry capability flags: a11y, interactive, responsive, rtl, darkMode, localized.

Guidance

Scannable data display

Present data for rapid scanning.

Use when

  • Use to summarize, sequence, or present data so users can scan it quickly.
  • Rendering a LaTeX mathematical expression as typeset math, inline or as a centered block.

Avoid when

  • Do not add display-only ornament when the user needs actionable structure or exact comparison instead.
  • The content is plain text with no mathematical notation; render it as normal text instead. Citing a source rather than typesetting math; use citation instead.

Use instead

  • Table for exact comparison
  • Plain text for a single value

Anti-patterns

  • Adding display ornament without informational value
Anatomy
root
Theming hooks
equation

Accessibility

WCAG 2.2 AA Keyboard focus-visible RTL-ready Localized labels Dark mode
Keyboard
managed
Focus
none
  • The root carries role=math and an aria-label combining 'Mathematical equation' with the raw expression, so assistive tech gets a readable description even before KaTeX renders.
  • Semantic HTML and a stable data-slot attribute for styling and scripting hooks.
  • Focus-visible rings use the ring token, so keyboard focus is always visible.
  • Disabled and invalid states are conveyed to assistive tech, not by color alone.
  • Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
  • Labels go through __() and layout uses logical properties (ms-*, text-start), so it mirrors under dir="rtl" — flip the preview to RTL to confirm.
  • Dark mode uses the same semantic tokens under the dark class; high contrast follows forced-color system tokens.

Livewire

Needs wire:ignore

Add wire:ignore to the component root because its behavior owns rendered DOM.

livewire-component.blade.php Blade
<div wire:ignore>
    <div class="flex flex-col items-center gap-4 p-6">
        <x-ui.equation expression="e^{i\pi} + 1 = 0" />
        <p class="text-sm text-muted-foreground">
            {{ __('Inline math like') }} <x-ui.equation :inline="true" expression="a^2 + b^2 = c^2" /> {{ __('flows with the text.') }}
        </p>
    </div>
</div>

Source

The exact, editable files ui:add writes into your app. Previews render this same code; there are no preview-only components.

resources/views/components/ui/equation.blade.php Blade
@props([
    'expression' => 'E = mc^2',
    'inline' => false,
])

{{--
    Equation: renders a LaTeX mathematical expression via KaTeX.

    The raw expression is embedded in `data-equation` on the root element and
    shown as initial text content inside the render target (no-JS / SSR fallback).
    Alpine's `uiEquation` init() reads both attributes and replaces the text with
    the KaTeX-rendered HTML output.

    Modes:
      - Block (default, inline=false): centered display equation with vertical
        margin; displayMode:true in KaTeX.
      - Inline (inline=true): inline-flex, baseline-aligned; displayMode:false.

    KaTeX CSS is imported by equation.js so the build bundles it automatically.
    KaTeX's internal stylesheet is third-party and exempt from the registry's
    semantic-token rules; the token classes here govern only our wrapper chrome.
--}}
@php
    $wrapperClass = $inline
        ? 'inline-flex items-baseline align-baseline'
        : 'my-3 flex justify-center';
@endphp

<span
    data-slot="equation"
    data-equation="{{ $expression }}"
    data-inline="{{ $inline ? 'true' : 'false' }}"
    x-data="uiEquation()"
    role="math"
    {{ $attributes->merge(['class' => $wrapperClass]) }}
    aria-label="{{ __('Mathematical equation') }}: {{ $expression }}"
>
    {{--
        Token-styled container that wraps the KaTeX output.
        bg-card + border-border give a subtle frame in both block and inline modes.
        The padding and rounding differ between inline and block for readability.
    --}}
    <span
        x-ref="render"
        class="{{ $inline ? 'rounded border border-border bg-card px-2 py-0.5 text-sm' : 'rounded-md border border-border bg-card px-4 py-3 text-base' }}"
    >{{-- Initial text fallback (shown before JS runs / for no-JS) --}}<code class="font-mono text-sm text-foreground">{{ $expression }}</code></span>
</span>
resources/js/ui/equation.js JS
/**
 * Equation behavior (uiEquation).
 *
 * Renders a LaTeX mathematical expression into a KaTeX output span. The raw
 * expression is stored in a data attribute on the root element so it is
 * available immediately (for no-JS and for the JS init), and JS replaces it
 * with the rendered KaTeX HTML.
 *
 * Display modes:
 *   inline=false (default) — block-level display equation (displayMode: true),
 *                            centered with vertical margin.
 *   inline=true            — inline-flex, baseline-aligned, no display mode.
 *
 * Graceful fallback: if KaTeX throws (malformed expression), the raw LaTeX
 * expression is shown in a <code> element rather than leaving a blank space.
 *
 * Note on KaTeX CSS: `katex/dist/katex.min.css` is imported here so the build
 * bundles it automatically. KaTeX's internal styles may include colour literals
 * inside *KaTeX's own published CSS* — that is third-party CSS, not our source,
 * and is exempt from the registry's raw-colour guard which scans source files.
 *
 * Teardown: no global listeners are added, so destroy() is a no-op.
 *
 * Self-registers on `alpine:init` so import order does not matter — import this
 * file once from your bundle (e.g. resources/js/ui/index.js).
 */
import katex from 'katex';
import 'katex/dist/katex.min.css';

document.addEventListener('alpine:init', () => {
    window.Alpine.data('uiEquation', () => ({
        init() {
            const el = this.$el;
            const expr = el.dataset.equation ?? '';
            const isInline = el.dataset.inline === 'true';
            const target = this.$refs.render;

            if (!target) {
                return;
            }

            try {
                const html = katex.renderToString(expr, {
                    throwOnError: true,
                    displayMode: !isInline,
                    output: 'html',
                    trust: false,
                    strict: 'warn',
                });
                target.innerHTML = html;
            } catch (_err) {
                // Fallback: show the raw expression in a code element.
                const code = document.createElement('code');
                code.textContent = expr;
                target.innerHTML = '';
                target.appendChild(code);
            }
        },

        destroy() {},
    }));
});

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.0.4
Status
Stable
License
open
Accessibility reviewed
No review date recorded
Last breaking change
No date recorded
Deprecation
Not deprecated
Contract
v6
Foundation
≥ 1.0.0