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>