Skins
A skin is the interface's look: a value for each design token, the CSS variables (--r-*) that every
component is drawn with, for the light theme and for the dark. Shared tokens (corners, fonts, text
size, motion) have one value for both. A skin builds on a built-in skin, its base, and holds only
the values it changes.
Each user picks a skin under Settings → Appearance, kept in their browser. Until they do, they
get the installation's default: the CMS setting Skins.Default, else Default (windows). The
built-in skins:
| Skins | Keys |
|---|---|
| Default, WCAG (high contrast), Neutral | windows, wcag, neutral |
| Relatude, Relatude BW, Relatude Classic | relatude, relatude-bw, relatude-classic |
| Forest, Winter, Synthwave, with moving backgrounds | forest, winter, synthwave |
| Mac System 1, Windows NT, iOS, WordPress, Optimizely, Paper, Terminal | mac, nt, ios, wordpress, optimizely, paper, terminal |
The shared token themes says which themes a skin has: both, light or dark. A skin with one
theme keeps the interface in it and hides the light and dark switch. A skin inherits themes from
its base, so set it to both when you start from a skin with one theme, such as Windows NT.
Making a skin. Administrators open the skin designer with New skin under Settings → Appearance, or with Make a copy to edit in a skin's menu. The skin being designed is in force while the designer is open, so every change shows at once. Basic mode turns a few dials (accent, tint, wallpaper, moving background, transparency, corners, depth, font and motion), each setting many tokens. Advanced mode edits every token and shows the skin as JSON, to copy to another installation. Describe a skin asks an AI for a skin from a sentence, such as warm and calm, sand and terracotta. It uses the database's AI settings, else the Relatude AI service on the license.
Save keeps the skin in the CMS settings (Skins) for every user, and a skin's menu makes it the
default or deletes it. The API's skins, skin-save, skin-delete, skin-default, skin-logo and
skin-generate commands do the same from code (chapter 17). As JSON, a skin names
its base and the values it sets in each theme, by token name without --r-. Shared tokens go in
light:
{
"name": "Harbour",
"description": "Sea blue and sand.",
"base": "windows",
"light": {
"accent": "#0b6e8a",
"accent-strong": "#07495c",
"bg": "#eef3f4",
"radius": "10px",
"font-ui": "Georgia, serif"
},
"dark": {
"accent": "#5cc2de",
"accent-strong": "#9be0f2",
"bg": "#0d1b22"
}
}| Group | Main tokens |
|---|---|
| Ground | bg and bg-paint (the window), login-paint, layer (the blades), surface (cards) |
| Text and lines | ink, muted, faint, line |
| Accent and status | accent, accent-strong, accent-contrast, accent-soft, danger, ok, warn |
| Controls and fields | ctrl, ctrl-hover, focus, field, field-line, switch-on |
| Menu and top bar | bar, bar-ink, nav-paint, nav-active, tab-selected |
| Blades | blade-head, blade-line, blade-gap, layer-opacity, layer-blur |
| Shape and type | radius, shadow-1, font-ui, font-mono, text-scale |
| Motion | ease, dur |
Further groups cover the selection, the preview, dialogs, logos and the moving background.
skinTokens in @relatude/ui lists every token with its group, label and help.
Pictures and logos. Tokens ending in -image hold SVG source: bg-image (the wallpaper),
layer-image (a texture on every blade) and nav-image (an illustration under the menu). Logos
replace Relatude's on the login screen (logo-login), in the top bar (logo-bar) and in the top bar
with the menu collapsed (logo-bar-collapsed). You upload them in the designer, as PNG, JPEG, WebP,
GIF or SVG of up to 1 MB.
What a value can be. A skin changes the look and nothing else. Every value is checked when it is saved and again before it is drawn:
- A CSS value is one value on one line, up to 400 characters: a colour, gradient, length, shadow,
font-family list, time, timing function, or
var(--r-name). It cannot contain;, braces,<,>,\,!,@, comments orurl(). - A picture is one
<svg>of up to 16,000 characters: shapes, gradients, patterns and filters, with no scripts, event attributes, styles or external files, and links only to its own parts (#id). It is drawn as an image, so it runs and loads nothing. - Fonts cannot be loaded from the web: use fonts on the computer, or IBM Plex Sans and IBM Plex Mono, which the interface loads.
Moving backgrounds
A skin can have a scene: an animation drawn with WebGL 2 behind the blades and the login screen,
under the wallpaper. Without WebGL 2 the window's paint shows instead. The scene token is none
(the default), peaks (a flight over faceted peaks), snow (snowfall over hills), aurora
(northern lights), grid (a neon grid), stars (deep space), forest (a forest against the light),
winter (a frozen lake), synthwave (a neon road toward a striped sun), graph (drifting linked
rings) or shader (the skin's own, below).
| Token | Is |
|---|---|
scene |
the scene, per theme |
scene-sky, scene-horizon, scene-land, scene-shade, scene-snow, scene-accent |
its colours, per theme: the sky at the top and at the horizon, the land in light and in shadow, snow and stars, the glow |
scene-veil |
a colour over the whole scene to quiet it, such as rgb(0 0 0 / 0.3) |
scene-speed |
1 as made, up to 4; 0 is a still picture |
scene-work-speed |
the share of that speed once signed in: 1 the same, 0.1 barely moving |
scene-work-opacity |
how much shows once signed in: 1 all, 0 none |
scene-amount |
how much is in it (flakes, stars, lights): 0 to 3, 1 as made |
scene-shader |
the GLSL of the shader scene |
Choosing a scene in the designer's Basic mode fills in its own colours, which scenes in
@relatude/ui lists. With scene-work-opacity at 0, a scene is for the login screen alone: it
fades out at sign-in and is not drawn after that. Give the login screen a login-paint close to the
scene's colours, since the scene fades in over it:
{
"name": "Night watch",
"base": "windows",
"light": { "themes": "dark", "scene-work-opacity": "0" },
"dark": {
"scene": "aurora",
"scene-sky": "#050b16", "scene-horizon": "#0f2233", "scene-land": "#18283a",
"scene-shade": "#070d16", "scene-snow": "#dfe9f5", "scene-accent": "#4dffb0",
"login-paint": "#050b16"
}
}To let a scene show through once signed in, set layer-opacity below 1 (down to 0.3) with
layer-blur, and give nav-paint and bar translucent colours, blurred by nav-blur and
bar-blur. Scenes ignore the system's reduce-motion setting: set scene-speed to 0 for a still one.
A shader of your own. With scene set to shader, the interface draws scene-shader, a GLSL ES
3.00 fragment shader in the style of Shadertoy:
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec3 col = mix(uHorizon, uSky, uv.y);
for (int i = 0; i < 5; i++) {
float f = float(i);
float y = 0.18 + f * 0.07 + 0.035 * sin(uv.x * (3.0 + f) + iTime * (0.25 + f * 0.07) + f * 1.7);
col = mix(col, mix(uLand, uShade, f / 5.0), smoothstep(y + 0.004, y - 0.004, uv.y) * 0.55);
}
fragColor = vec4(col, 1.0);
}iResolution,iTime,iTimeDelta,iFrame, the coloursuSky,uHorizon,uLand,uShade,uSnowanduAccent(vec3),uAmount(scene-amount) anduNight(1 when the horizon is dark) are declared for you.- The source is plain ASCII, at most 12,000 characters, with no
#versionor#extension. Alpha is ignored. - It runs on the graphics card at half resolution and can read nothing: not the page, the network or anything of the user's. A shader that stops the graphics card is switched off.
The designer compiles the shader as you type and shows the errors. exampleShaders in
@relatude/ui has more to start from.