Skip to content

Theming ​

Meridiana has no stylesheet of its own. Every element it draws takes its Tailwind classes from a slot of the theme (event, dayHeader, monthCell, …). States are not separate classes: the element carries data-* attributes and the theme styles them with Tailwind variants, for example data-today:bg-indigo-50 or data-dragging:opacity-80. A variant is more specific than a plain utility, so a state always wins over the base style of its slot, without tailwind-merge.

There are three levels of customization.

1. Colors only: CSS variables ​

The default web theme reads its colors from the --mer-* variables of @moobitech/meridiana, which you already import. Override what you need after it:

css
@import '@moobitech/meridiana';

:root {
  --mer-accent: #0f766e;
  --mer-today: #f0fdfa;
  --mer-default-event-bg: #ccfbf1;
  --mer-default-event-border: #0f766e;
  --mer-default-event-text: #134e4a;
}
VariableUsed for
--mer-surface, --mer-surface-alt, --mer-surface-hoverbackgrounds: grid, weekends and past days, hover
--mer-borderlines and borders
--mer-text, --mer-text-mutedtext, secondary text
--mer-weekendweekend day names
--mer-todaybackground of today
--mer-accent, --mer-accent-contrasttoday's number, active buttons
--mer-nowthe current-time line
--mer-ok, --mer-warning, --mer-dangerstatus dots, invalid previews, danger menu entries
--mer-default-event-bg, --mer-default-event-border, --mer-default-event-textcolors of the events without a color of their own

Dark mode follows the .dark class: the tokens have a dark palette under .dark. Add @custom-variant dark (&:where(.dark, .dark *)); to your Tailwind entry to toggle it with the class.

The colors of a single event (event.color) arrive on that event as --mer-event-border and --mer-event-color (primary), --mer-event-bg (secondary) and --mer-event-text (text). The default theme reads them first and falls back to --mer-default-event-*. Never set --mer-event-* on :root: it would replace the fallback of every event without that color.

2. Small changes: extend the default theme ​

createMeridianaTheme builds a complete theme from the slots you give. In extend mode your classes are appended to the default ones:

ts
import { createMeridianaTheme } from '@moobitech/meridiana-core';

const theme = createMeridianaTheme(
  { event: 'font-semibold shadow-md', dayHeaderDate: 'text-2xl' },
  { mode: 'extend' },
);

Pass it to the provider (provideMeridiana({ theme }), <MeridianaProvider theme={theme}>) or to the theme input of a single view.

3. Your design system: replace the slots ​

Give the slots your own classes: each slot you pass replaces the default classes (replace mode, the default). This is how an admin kit or a company design system gets a calendar that looks native to it:

ts
provideMeridiana({
  theme: {
    event:
      'absolute overflow-hidden rounded-lg border-l-4 bg-white px-2.5 py-1.5 text-xs shadow-xs ' +
      'data-dragging:shadow-lg data-invalid:ring-2 data-invalid:ring-red-500 data-stacked:ring-1 data-stacked:ring-white',
    eventTitle: 'line-clamp-2 font-medium text-slate-700',
    dayHeader: 'flex flex-col items-center py-2 data-today:text-primary',
    // …
  },
});

Keep the layout classes of the slots you replace (absolute on event, flex on dayHeader…): the defaults in meridianaDefaultTheme are a good starting point to copy.

To use the colors of the events, read their variables with a fallback of your design system, for the events whose color lacks that part:

ts
event: 'border-l-4 border-[var(--mer-event-border,var(--color-primary))] bg-[var(--mer-event-bg,white)] …',

Slots and states ​

SlotElementdata-* states
rootthe host of every view
toolbar, toolbarTitle, toolbarButton, viewSwitcher, viewButtonnot drawn by the views: use them in your own toolbar to matchtoolbarButton: disabled; viewButton: active
dayHeader, dayHeaderWeekday, dayHeaderDateday headers of the time gridtoday, past, weekend
timeGrid, timeGridHeader, timeGutter, timeLabel, allDayRowtime grid frame, hours
dayColumna day columntoday, past, weekend
slot, slotLabela segment of a columnhour-start, disabled
nowIndicatorthe current-time line
eventan event box, in every view's time grid and all-day rowdragging, invalid, disabled, starts-before, ends-after, all-day, stacked, narrow; React Native also selected
eventTitle, eventTime, eventDescriptiontexts of an event
eventStatusthe status dotok, warning, danger
eventMenuButtonthe "⋯" button
resizeHandlea resize handleedge-start, edge-end
contextMenu, contextMenuItemmenu and its entriescontextMenuItem: disabled, danger
monthGrid, monthWeekdayMonth frame, weekday names
monthCella day of the Monthtoday, past, weekend, outside
monthDayNumberits numbertoday, outside
monthEvent, monthMorean event row, the "+N" linkmonthEvent: disabled
agenda, agendaDay, agendaDayHeaderAgenda frame, a day, its headeragendaDayHeader: today, weekend
agendaItem, agendaItemTimean Agenda row, its timeagendaItem: all-day, disabled
agendaEmptythe empty message
eventDotthe color dot (Month, Agenda)

The list is also exported as MERIDIANA_SLOT_STATES.

Tailwind sources ​

The theme classes live in the JavaScript of the packages and in your theme file, so Tailwind must scan both. Tailwind v4 detects your own sources but skips node_modules; import @moobitech/meridiana for the packages (see Getting started). Import it with your own theme too: the components carry layout classes that no slot replaces.

css
@import 'tailwindcss';
@import '@moobitech/meridiana';

Without it there is no error: the classes that your app does not use elsewhere are missing, and some elements are out of place.

React Native ​

React Native has no cascading text styles and no per-element CSS variables, so the native theme (meridianaNativeTheme) differs in three ways:

  • Text slots: elements that hold a label have a separate slot for the text: contextMenuItemText, monthDayNumberText, monthEventText, agendaDayHeaderText, agendaItemText. Text classes (text-*, font-*) go on the text slots.
  • States are data-* props with the value true, as Uniwind expects; the data-today: variants are written the same way.
  • Event colors are applied as styles, over the theme classes.

The default native theme uses the color tokens of gluestack-ui (bg-background, text-foreground, text-muted-foreground, border-border, bg-primary, bg-muted, text-destructive, …): an Expo app with gluestack-ui and Uniwind gets a calendar in its colors and dark mode. Without gluestack-ui, define those colors in your Uniwind theme or replace the slots:

tsx
import { MeridianaProvider } from '@moobitech/meridiana-native';

<MeridianaProvider theme={{ event: 'absolute overflow-hidden rounded-xl border-l-4 bg-white px-2 py-1' }}>
  …
</MeridianaProvider>;

Commercial software, free for the web with the Community plan. License agreement