Reference
Janela publishes the hooks and a theme supplies the taste (ADR 036).
janela.css is the grid, the pane's own markup and enough style to be legible;
vitral.css is one theme, and this whole site wears it. Everything below is drawn
live against this demo's own data, with the classes and properties that produced it named beside
it. The full contract lists every one.
Link the first always, the second only if you want this look.
<%= stylesheet_link_tag "janela" %>
<%= stylesheet_link_tag "vitral" %>
<body class="vitral">
Nothing is repainted until that class is present, so linking the stylesheet can never be the thing
that broke a page. A theme of your own is the same one line: Janela.theme = "midnight"
resolves midnight.css from your own asset paths, and vitral is only the one the gem ships.
janela-cols-1 to 12, janela-gap-0 to 8, and
janela-span-1 to 12. A frame saved as a record gets them from its own stored
integers, so nothing an analyst types reaches CSS as a length: the number selects a rule that is
already written (ADR 016). On a page you compose yourself, you write the same class names.
<%= janela_frame do %>
<div class="janela-frame janela-cols-3 janela-gap-4">
<%= janela_pane Order, :revenue %>
...
</div>
<% end %>
three columns, gap 4
the same grid, with the first pane wrapped in janela-span-2. A pane is the grid item, and
janela_pane takes no class of its own, and will not, so a wrapper is how a hand composed
page spans one. A stored frame does it from the integer on the pane row instead. Arranging panes
beyond the shipped grid is the host's.
Below 40rem it collapses to one column, from the stylesheet rather
than from the data: a dashboard nobody can read on a phone is not a choice worth offering.
What janela_pane puts in your own page, whatever the renderer:
janela-pane on the table, the value's <p> or the chart's
<figure>, plus janela-value, janela-value-label
and janela-value-number on a single value, janela-chart on the
canvas inside its figure and janela-chart-title on its figcaption, and
janela-empty or janela-error when there is nothing to draw. A
table pane also renders a <caption> and a chart pane a
<figcaption>, each its own accessible name (ADR 042).
Put janela-own-headings on any ancestor and a caption and a single value's label stop
being drawn, without leaving the accessibility tree. It hides rather than removes on purpose: a table
with no caption has no accessible name, so display: none would land a screen reader on a
grid of numbers with nothing to say what they measure (#26).
Without it
Janela writes the caption: Revenue by Status.
With janela-own-headings
Janela's caption is gone from the page and still in the accessibility tree. The heading above the
table is this page's own <h3>, saying something Janela would never have written.
Both tables still announce "Revenue by Status" to a screen reader, which is the whole point: the second one has a visible heading of its own and an accessible name underneath it, rather than one heading doing both jobs badly.
A chart pane's title used to be only an aria-label, announced and never seen, which
janela-own-headings had nothing to hide. Since ADR 042 it is a real
<figcaption>, so the same rule now hides it the same way.
Without it
Janela writes the figcaption: Revenue by Status.
With janela-own-headings
Janela's figcaption is gone from the page and still in the accessibility tree; the canvas's
aria-labelledby points at it either way, hidden or not.
Both charts still announce "Revenue by Status" to a screen reader, same as the tables above.
A bar, a doughnut slice or a pie slice is drawn in the colour for its position, first to eighth, and
in one neutral for everything after the eighth. The colours are custom properties, so a theme sets
them: --janela-series-1 to -8 and --janela-series-other. Slot 1
follows --janela-accent. These are vitral's, drawn from the live styles.
--janela-series-1
--janela-series-2
--janela-series-3
--janela-series-4
--janela-series-5
--janela-series-6
--janela-series-7
--janela-series-8
--janela-series-other
A ring suits a handful of values. The bar chart is revenue by month, thirteen bars, so the last five share the neutral: a ninth bar in the first bar's colour would be two categories drawn the same. The order is what keeps neighbours apart, checked together for colour vision deficiency, so a reorder has to be checked again (ADR 046).
The same nine names under Janela's own neutral set, which is what a host gets without vitral. A theme or a host replaces the set by setting the properties and nothing else:
:root {
--janela-series-2: #c93c54;
--janela-series-3: #7d5bd1;
/* ... through -8, and --janela-series-other */
}
--janela-series-1
--janela-series-2
--janela-series-3
--janela-series-4
--janela-series-5
--janela-series-6
--janela-series-7
--janela-series-8
--janela-series-other
A few custom properties carry janela.css: --janela-space, the base unit of
the whole spacing scale, --janela-line and --janela-accent, with the chart
palette above. Set them and everything that depends on them moves.
:root {
--janela-space: 0.3rem;
--janela-accent: #7c3aed;
}
the same panes, under a block that sets those two properties and nothing else; the bar chart's
colour moves with --janela-accent too, rather than staying Chart.js's own blue (#62)
Vitral has its own, and the same rule applies: retheme from the properties rather than by forking the
stylesheet. The light is --vitral-light-cobalt, -teal, -amber,
-rose and -violet; the glass is --vitral-pane-1 through
-5; the leading is --vitral-came. Its own public classes are
vitral-pane, vitral-panes, vitral-button,
vitral-button-primary and vitral-lattice, so the page around a dashboard can
be made of the same window. Every card on this site is one.
janela.css also styles Janela's own pages, the frame index and the editing forms. Those
names are the engine's own chrome rather than an interface, and may change in any release
(ADR 036). They are plain class names the engine's own pages use, not scoped to
janela-page, so they affect your pages only if your markup uses the same names. Avoid them.
docs/theming.md says which names are the contract and which are not.