::: layout landing-hero title=”” title2=”” primaryLabel=”” secondaryLabel=”npm i @beforesemicolon/markup”

=== copy

=== stat

##

=== stat

0

=== stat

100%

=== code filename=counter.js lang=javascript

import { html, state, effect } from '@beforesemicolon/markup'

const [count, updateCount] = state(0)

const doubleCount = () => count() * 2

effect(() => {
    console.log(count())
})

const countUp = () => updateCount((prev) => prev + 1)
const countDown = () => updateCount((prev) => prev - 1)

const App = html`
    <h1>Conunter</h1>
    <p><strong>Current count</strong>: ${count}</p>
    <p><strong>Double count</strong>: ${doubleCount}</p>
    <button type="button" onclick="${countDown}">-</button>
    <button type="button" onclick="${countUp}">+</button>
`

App.render(document.getElementById('app'))

:::

::: layout landing-ecosystem

=== header

##

=== product title=”” package=@beforesemicolon/web-component color=orange icon=webComponents href=https://web-component.beforesemicolon.com/

=== product title=Router package=@beforesemicolon/router color=cyan icon=router href=https://router.beforesemicolon.com/

=== product title=Intl package=@beforesemicolon/intl color=primary icon=reactive href=https://intl.beforesemicolon.com/

:::

::: layout landing-features

=== header

##

=== feature icon=reactive

###

=== feature icon=tiny

###

=== feature icon=standards

###

=== feature icon=plug

###

=== feature icon=webComponents

###

=== feature icon=surgical

###

:::

::: layout landing-showcase

=== header

##

=== example label=”” color=orange filename=todos.js lang=javascript

import { html, state, effect, repeat } from '@beforesemicolon/markup'

const [todos, setTodos] = state(
    JSON.parse(localStorage.getItem('todos') ?? '[]')
)

effect(() => {
    localStorage.setItem('todos', JSON.stringify(todos()))
})

const addTodo = () => {
    const text = window.prompt('What needs doing?')?.trim()

    if (text) setTodos((prev) => [...prev, { text, done: false }])
}

const toggle = (i) =>
    setTodos(todos().map((t, idx) => (idx === i ? { ...t, done: !t.done } : t)))

html`
    <button type="button" onclick="${addTodo}">Add</button>
    <ul>
        ${repeat(
            todos,
            (todo, i) => html`
                <li
                    class="${todo.done ? 'done' : ''}"
                    onclick="${() => toggle(i)}"
                >
                    ${todo.text}
                </li>
            `
        )}
    </ul>
`.render(document.querySelector('#app'))

=== example label=”” color=cyan filename=button-component.js lang=javascript

import { WebComponent, html } from '@beforesemicolon/web-component'
import stylesheet from './button.css' with { type: 'css' }

class Button extends WebComponent {
    static observedAttributes = ['disabled', 'type']

    type = 'button'
    disabled = false

    stylesheet = stylesheet

    handleClick = (evt) => {
        evt.stopPropagation()
        this.dispatch('click')
    }

    render = () => {
        return html`
            <button ${this.props} class="btn" onclick="${this.handleClick}">
                <slot></slot>
            </button>
        `
    }
}

customElements.define('bfs-button', Button)

=== example label=”” color=orange filename=profile.js lang=javascript

import { html, suspense } from '@beforesemicolon/markup'

const loadUser = async () => {
    const res = await fetch('/api/me')
    return res.json()
}

const renderUser = async () => {
    const user = await loadUser()
    return html`
        <article>
            <h2>${user.name}</h2>
            <p>${user.bio}</p>
        </article>
    `
}

html`
    <h1>Profile</h1>

    ${suspense(
        renderUser,
        html`<p>Loading profile...</p>`, // fallback
        (err) => html`<p>Failed: ${err.message}</p>` // catch
    )}
`.render(document.querySelector('#app'))

=== example label=”” color=cyan filename=app.html lang=html

<!-- in <head>:
<script src="https://unpkg.com/@beforesemicolon/router/dist/client.js"></script>
-->

<nav>
    <page-link path="/">Home</page-link>
    <page-link path="/about">About</page-link>
    <page-link path="/users">Users</page-link>
</nav>

<page-route path="/">
    <h1>Welcome home</h1>
</page-route>

<page-route path="/about" src="./pages/about.js"></page-route>

<page-route path="/users" exact="false">
    <page-route src="./pages/users.js"></page-route>
    <page-route path="/:userId" src="./pages/user.js"></page-route>
</page-route>

<page-route path="/404"> 404 - Page not found! </page-route>

<page-redirect path="/404" title="404 - Page not found!"></page-redirect>

=== example label=”” color=orange filename=timer.js lang=javascript

import { html, state } from '@beforesemicolon/markup'

const [seconds, setSeconds] = state(0)

html` <p>Elapsed: ${seconds}s</p> `
    .onMount(() => {
        // runs once when attached to the DOM
        const id = setInterval(() => setSeconds(seconds() + 1), 1000)
        return () => clearInterval(id)
    })
    .onUpdate(() => {
        // runs every time a tracked value changes
        console.log('tick', seconds())
    })
    .render(document.querySelector('#app'))

:::

::: layout landing-install

=== header

##

=== tab key=cdn label=CDN command=”<script src="https://unpkg.com/@beforesemicolon/markup/dist/client.js"></script>”

=== tab key=npm label=npm command=”npm install @beforesemicolon/markup”

=== tab key=yarn label=yarn command=”yarn add @beforesemicolon/markup”

=== tab key=pnpm label=pnpm command=”pnpm add @beforesemicolon/markup”

:::

::: layout landing-cta title=”” title2=””

=== copy

:::