Home SVAR Blog Using SVAR Svelte Calendar with Tailwind CSS and daisyUI

Using SVAR Svelte Calendar with Tailwind CSS and daisyUI

 

Olga Tashlikovich ·

Sep 3 · 10 min read

Using SVAR Svelte Calendar with Tailwind CSS and daisyUI

In May, we released SVAR Svelte Calendar, a lightweight open-source event calendar that brings a Google Calendar-like scheduler to Svelte and SvelteKit apps: day, week, and month views, drag-and-drop event editing, multiple calendars, a custom edit form, and event customization. An optional PRO Edition adds resource management views and recurring events.

Like other SVAR components, the calendar doesn’t force a styling approach on your project. It ships with prebuilt styles via customizable CSS variables, its own light and dark themes, and requires no CSS framework to look great. At the same time, it can coexist comfortably with the CSS pattern and UI library your project already uses. This article demonstrates that.

Calendar Demo with Tailwind CSS & daisyUI

Recently, our user Fernando Humanes shared on our forum that he’d built a working demo that pairs SVAR Svelte Calendar with Tailwind CSS and daisyUI. We thought it was a great chance to show how our Svelte calendar component can be used with these popular libraries.

The example renders database records as calendar events, with day, week, and month views, event details displayed in daisyUI drawers, and data loaded on demand from a MySQL database through a PHP Slim backend.

SVAR Svelte Calendar - Demo with Tailwind

Fernando documented the whole exercise in a detailed tutorial on his blog in Spanish, and this post walks through it in English with the actual code.

Tailwind CSS and daisyUI in brief

Tailwind CSS is a utility-first CSS framework, used broadly, from headless libraries like Melt UI to component kits like Skeleton and daisyUI. It allows you to compose styles directly in your markup from small utility classes like flex, p-4, rounded-lg – instead of writing custom CSS classes. This keeps styles close to the code that uses them and eliminates a whole class of CSS maintenance.

daisyUI is a component library built on top of Tailwind. It includes class-based components (like buttons, cards, modals, drawers, simple tables) with a set of ready-made themes, so you get a consistent interface without writing component CSS or JavaScript.

The combination of both is popular with Svelte developers because it makes UI iteration fast: markup stays readable, and design stays consistent.

The Integration, Step by Step

Fernando’s starting point was a Svelte 5 project from his earlier tutorial: a reusable DataGrid component styled with daisyUI, powered by a PHP Slim API. The idea for this exercise was simple — take the same data and render it as a calendar instead of a grid. The whole example is written with Svelte 5 runes ($state, $derived, $props).

Swapping the component

The project reuses everything from the DataGrid solution; the DataGrid component is replaced with the SVAR Calendar, and the rest of the app — pages, navigation, drawers — stays untouched. Here’s what the calendar module imports from the SVAR packages:

<script>
import { Calendar, Willow, ContextMenu } from "@svar-ui/svelte-calendar";
import { Locale } from "@svar-ui/svelte-core";
import { en as enCore, es as esCore } from "@svar-ui/core-locales";
import { en, es } from "@svar-ui/calendar-locales";
import EventCard from "./EventCard.svelte";
import EventDrawer from "./EventDrawer.svelte";
</script>

Note the localization setup: Locale (SVAR Core) receives merged dictionaries from @svar-ui/calendar-locales and @svar-ui/core-locales, so the calendar UI is fully bilingual (Spanish/English) with a single state variable. The component is wrapped in {#key locale}, which re-renders the calendar when the language switches.

Views and weekend highlighting

The calendar component comes with day, week, and month views out of the box. Fernando narrows the working hours for the time-based views and uses the cellCss callback to highlight weekends and holidays:

const views = [
{
id: "month",
sections: {
month: {
yScale: {
visible: true,
format: "weekNumberFormat"
}
}
}
},
{
id: "week",
sections: {
timeGrid: {
yScale: { startHour: 9, endHour: 17 }
}
}
},
{
id: "day",
sections: {
timeGrid: {
yScale: { startHour: 9, endHour: 17 }
}
}
}
];
// Configuración de celdas para resaltar fines de semana y días festivos
function cellCss(ctx) {
const { date } = ctx;
if (!date) return "";
const day = date.getDay();
if (day === 0 || day === 6) return "weekend";
if (day === 5) return "holiday";
return "";
}

The returned class names are styled with a couple of global rules: light purple for weekends, light red for holidays.

Loading events per visible range

Fernando also shared a performance pattern that keeps the calendar fast even with large numbers of events.

In his demo, the calendar doesn’t fetch all events at once: when the view changes or the user navigates to another date range, the app computes the visible range and requests only the events that fall into it. getRangeFromView turns the current view into MySQL-formatted start/end timestamps:

function getRangeFromView(baseDate, view) {
const start = new Date(baseDate);
const end = new Date(baseDate);
if (view === "day") {
// Día completo
start.setHours(0, 0, 0, 0);
end.setHours(23, 59, 59, 999);
} else if (view === "week") {
// Semana completa (lunes a domingo)
const day = start.getDay(); // 0=domingo, 1=lunes...
const diff = (day === 0 ? -6 : 1 - day); // mover al lunes
start.setDate(start.getDate() + diff);
start.setHours(0, 0, 0, 0);
end.setTime(start.getTime());
end.setDate(start.getDate() + 6);
end.setHours(23, 59, 59, 999);
} else if (view === "month") {
// Primer día del mes
start.setDate(1);
start.setHours(0, 0, 0, 0);
// Último día del mes
end.setMonth(start.getMonth() + 1);
end.setDate(0); // día 0 = último del mes anterior
end.setHours(23, 59, 59, 999);
}
return {
start: toMySQL(start),
end: toMySQL(end)
};
}

The onnavigateto hook fires on every view or date change, so the handler updates the base date and view, recomputes the range, and refetches:

<Calendar
events={eventsCalendar}
eventCss={cssByCalendar}
view="month"
views={views}
date={new Date()}
readonly={true}
init={handleInit}
eventPopup={EventCard}
onnavigateto={p => {
fechaBase = p.date ?? fechaBase;
vistaCalendar = p.view ?? vistaCalendar;
const range = getRangeFromView(fechaBase, vistaCalendar);
start = range.start;
end = range.end;
fetchEvents(); // llamada a tu backend para recargar eventos según el nuevo rango
}}
{cellCss}
/>

And fetchEvents sends that range to the backend:

async function fetchEvents() {
try {
const resEvents = await apiRest.post("/event/list", {
start: start,
end: end
});
events = resEvents.data;
filteredData = filterData();
} catch (err) {
errorMessage = "No se pudieron cargar los eventos.";
} finally {
loading = false;
}
}

This keeps the initial load light and the calendar fast as the dataset grows.

Mapping data to the calendar’s structure

Fernando feeds the same data to both the grid and the calendar. The original records stay untouched for the daisyUI drawers; a thin mapping layer reshapes them into what the SVAR Calendar component expects: text, start, end, allDay, calendarId.

One detail worth mentioning is the handling of all-day events. If you pass an all-day event as a plain date, SVAR Calendar will stretch it across the full 24 hours; if you clamp it to 01:00–23:00, the calendar renders as one tidy block (as it should).

eventsCalendar = filtered.map(e => {
const isAllDay = e.all_day === "1";
// Convertimos fechas
const start = new Date(e.start);
const end = new Date(e.end);
if (isAllDay) {
// Ajuste para evitar que SVAR Calendar reparta el día en 24 horas
start.setHours(1, 0, 0, 0); // 01:00:00
end.setHours(23, 0, 0, 0); // 23:00:00
}
return {
...e,
id: e.id,
text: e.title,
start,
end,
allDay: isAllDay,
calendarId: e.id_calendar, // clave para el color
};
});

Event cards with daisyUI

Clicking an event opens a popup card built with daisyUI, providing the same visual language as the rest of the app. In Fernando’s demo, the card shows event details, such as start/end time, description, calendar group (team, personal, celebrations), priority, location, and users assigned.

SVAR Svelte Calendar - daisyUI Popup

The component receives the event and a close callback through Svelte 5 $props. Below is a trimmed version with the key parts. The complete EventCard (including the Spanish date formatting) is in the source files on his blog:

<script>
// The popup is a plain Svelte component: it receives the event
// and a close callback via Svelte 5 props
const { event, close } = $props();
const headerStyle = `background-color: ${event.calendar_color}`;
const borderStyle = `border-color: ${event.calendar_color}`;
const people = event.names_people
? event.names_people.split(",").map(p => p.trim())
: [];
</script>
<div class="event-card w-80 rounded-lg shadow-xl bg-base-100 p-0 overflow-hidden border-2"
style={borderStyle}>
<!-- The header uses the event's calendar color -->
<header class="text-center text-white font-bold py-3" style={headerStyle}>
{event.title}
</header>
<div class="p-4 space-y-3">
<!-- daisyUI badges for people and resources -->
{#each people as p}
<span class="badge badge-primary badge-sm">{p}</span>
{/each}
<button class="btn btn-sm btn-outline" onclick={close}>Cerrar</button>
</div>
</div>

The rest is plain daisyUI markup — you can style the popup with any classes from your design system.

Adding and editing events

The calendar runs in readonly mode — Fernando turns off the built-in event editor and reuses the add/edit drawer from his DataGrid solution, so event management behaves exactly like in the grid. The ”+” button opens a daisyUI drawer with an empty event form. Saving the form posts the data to the same PHP Slim API (POST /event, PUT /event/{id}).

SVAR Svelte Calendar with Tailwind - Edit event

Right-clicking an event brings up a context menu with view, edit, and delete options:

const contextOptions = [
{ id: "view", text: "Ver", icon: "wxi-eye" },
{ id: "edit", text: "Editar", icon: "wxi-edit" },
{ id: "delete", text: "Eliminar", icon: "wxi-delete-outline" }
];
SVAR Svelte Calendar with Tailwind - Context menu

Deleting asks for confirmation through SweetAlert2 before calling the API. The ContextMenu component wraps the calendar and wires these actions to the same handlers the grid uses.

Per-calendar colors

The demo includes 3 types of calendars: Personal, Team, and Celebrations. Each calendar in the app has its own color. Instead of hardcoding rules, Fernando generates them at runtime: handleInit runs when the calendar initializes and injects a <style> block with one rule per calendar, covering all event renderers:

function handleInit(api) {
console.log("Calendar inicializado, generando CSS dinámico…");
const style = document.createElement("style");
style.innerHTML = calendars.map(c => `
.cal-${c.idsvar_calendar}.wx-box-event,
.cal-${c.idsvar_calendar}.wx-bar-event,
.cal-${c.idsvar_calendar}.wx-month-event,
.cal-${c.idsvar_calendar}.wx-month-box-event {
background-color: ${c.color} !important;
color: white !important;
}
`).join("\n");
document.head.appendChild(style);
}

Fernando notes that he deliberately wrote the code with comments throughout, so it works as a learning reference. All sources are available for download on his blog (find them at the bottom of the page):

  • Svelte 5 front end
  • PHP Slim 4 backend
  • MySQL database backup

The takeaway

SVAR Svelte Calendar can be easily integrated into a pure Svelte 5 or SvelteKit app. It works out of the box with its own themes — no CSS framework required.

However, if your project is already built around Tailwind CSS, daisyUI, or similar libraries, the calendar component fits right in without conflicts. The tutorial shows that you can also attach daisyUI components to adjust calendar functionality to fit your needs, like showing a popup with details on click. Fernando’s demo is a great reference for that.

Try the demo, download the sources, and see how SVAR Svelte Calendar behaves in your own stack:

Build great scheduling UIs with SVAR Svelte Calendar and your libraries of choice!

This guide is based on “S-028 – Integración SVAR Calendar con Tailwind CSS y Daisy UI en Svelte5” by Fernando Humanes, published with the author’s permission.