LumoCSS logoLumoCSS logo
Introduction

Quick start

To get started creating website with Lumo.CSS you can link the stylesheet and JavaScript code with cdnjs service. Paste code from below into your <head> section, and you are ready to go.

Lumo follows a simple authoring model: HTML elements describe objects, native and ARIA attributes describe behavior or state, class="" attributes describe Lumo design options, and layout helpers.

<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/y4ch0/Lumo.CSS/1.0.1/dist/lumo.min.css">
<script src="https://cdn.jsdelivr.net/gh/y4ch0/Lumo.CSS/1.0.1/dist/lumo.min.js"></script>

Working with web frameworks

Lumo's JavaScript scans the DOM once on load (initLumo) and attaches a single set of global listeners for modals, dropdowns, and navbars – these work automatically anywhere in your app since they're delegated on document.

Tabs, carousels, and dropdowns, however, are initialized per-element and marked with data-lumo-init so they aren't set up twice. When a framework renders new markup after the initial page load – a tab panel mounted after a route change, a carousel added conditionally, etc. – that new markup won't be picked up until you re-run the scan. Call window.initLumoScan() after your framework updates the DOM. It's safe to call as often as you like; already-initialized elements are skipped.

React

Load the script once (e.g. in your root layout), then re-scan after mount and after any update that adds new Lumo components.

"use client";
import { useEffect } from "react";

export default function TabsSection() {
    useEffect(() => {
        window.initLumoScan();
    }); // runs after every render — cheap no-op if nothing new

    return (
        <section data-tabs>
            {/* tab markup */}
        </section>
    );
}

Vue

Re-scan in onMounted and onUpdated so components added by v-if/v-for get initialized.

<script setup>
import { onMounted, onUpdated } from "vue";

onMounted(() => window.initLumoScan());
onUpdated(() => window.initLumoScan());
</script>

<template>
    <section data-tabs>
        <!-- tab markup -->
    </section>
</template>

Angular

Use AfterViewChecked to catch DOM changes from *ngIf/*ngFor and structural directives.

import { Component, AfterViewChecked } from "@angular/core";

@Component({
    selector: "app-tabs-section",
    templateUrl: "./tabs-section.component.html",
})
export class TabsSectionComponent implements AfterViewChecked {
    ngAfterViewChecked(): void {
        (window as any).initLumoScan();
    }
}

Note: only call initLumo() once per page (it guards itself against double-attaching global listeners, but there's no reason to invoke it more than once). Use initLumoScan() – not initLumo() – for re-scans after framework re-renders.

Starter HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lumo.CSS website</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/y4ch0/Lumo.CSS/1.0.1/dist/lumo.min.css">
    <script src="https://cdn.jsdelivr.net/gh/y4ch0/Lumo.CSS/1.0.1/dist/lumo.min.js"></script>
</head>
<body>
    <main>
        <div class="container">
            <h1>Hello World!</h1>
            <button>Lumo.CSS button</button>
        </div>
    </main>
</body>
</html>