Vanilla+ CSS Framework

Introduction

There are already plenty of CSS frameworks, with varying levels of complexity. This one attempts to be a progressive enhancement on plain HTML5, with CSS based improvements with occasional JS components where necessary. It strays from the extreme simplicity of the flat layered "paper" trend in favor of more obvious classic visual cues enhanced for modern browsers.

All styling on elements, attributes or classes not prefixed with vp- only take effect inside .vp blocks to help reduce collisions with other frameworks. You can further disable part or all of a .vp block with the exclusion class .xvp.

Compiled CSS files are located in dist/. You may use vpweb.css to get the entire framework at once, or just some of the individual files described below. Be sure to import core.css first in order to avoid priority side-effects.

Efficient

The entire framework, all components included, is smaller than a typical company logo image:

Size Gzipped Brotli
vpweb.css22.4KB5.1KB4.5KB
vpweb.js10.2KB4.3KB3.8KB

Browser Support

Currently compatible with Baseline's "Widely available" browser versions (30 months) which includes long term support versions as of 2025-01-01. Some features from "Newly available" may be used, but only when they degrade gracefully (i.e. content-visibility). This means we're refraining from using cutting-edge features like :has() and round().

Chrome/Edge/Opera
103 (2022-04-26)
Safari
16.4 (2023-03-27)
Firefox
101, ESR 91.10 (2022-05-31)

Build Process

On the CSS side, you may reference the pre-built vpweb.css alone or @import 'vpweb.css'; from your CSS sources. You might even want to import only the bits you want such as core.css and grid.css to use our grid.

If you use @layers, just set our pre-defined vp layer where you want in your stack. All our internal layers are namespaced within this single global layer.

Core core.css

Minimal, non-opinionated reset to help normalize output across browsers.

Colors

CSS variables for the followig colors are available in two variants: saturated RGB triplets as --vp-X and 5% white-dimmed hex as --vp-X-05 (e.g. --vp-red-05). Helper classes also set these colors for the current text and border if any. Optional .bg class sets a background color in the same hue at 5% opacity. Optional .boxed class sets a thin border at 40% opacity.

In some uses like notes, semantic terms are used instead of direct color names, like error implying red, etc.

.cyan .blue .violet .red .orange .yellow .green .dim

Typography

.mono
System and user-preferred monospace font families (automatically applied to code, kbd, pre, samp)
.sans
System and user-preferred sans-serif font families (automatically applied to buttons and inputs)
.serif
System and user-preferred serif font families

Density

Three classes are recognized by various modules (i.e. grid) as indicative of spacing to use between items. The closest parent or self has priority.

.tight
.tight
.tight
.spaced
.spaced
.spaced
.relaxed
.relaxed
.relaxed

Responsive

Certain classes restrict which devices can see an element, by viewport width or display type. Note that the names "mobile", "tablet" and "desktop" are used as nicknames for the three responsive size ranges, not as true device identification. CSS media queries use a "device pixel ratio" to define their own meaning for "px", so those are not actual pixels but industry standard values.

Mobile:
Less than 768px, .mobile-only, .tablet-down
Tablet:
From 768px to less than 992px, .tablet-only, .tablet-up, .tablet-down
Desktop:
From 992px up, .tablet-up, .desktop-only
Large Desktop:
From 1200px up, .largedesktop-only may occasionally be useful.
Pointer:
With no fine pointer, .mouse-only is hidden. When no pointer can hover, .hover-only is hidden.
Print:
On a screen, .print-only is hidden, while on any other device, .screen-only is hidden.

Helper classes

.gone
Sets display: none
.sticky
Sets position: sticky
.center
Sets text-align: center
.right
Sets text-align: right

Layouts layouts.css

Wrap .vp-wrap

Adds a max-width and side margins dictated by density (self or immediate parent).

Grids [vp-grid="N"]

Grids are defined by adding attribute vp-grid="x" to an element, where x is one of: 5, 8, 10 or 12. Cells in grids are immediate child elements, optionally with attribute vp-cols="x" where x is a number between 2 and 12 (1 being implicit). Rows wrap, so you only need to use multiple grid blocks for splitting incomplete lines or changing bases.

An <img> directly inside a [vp-grid] or wrapped in just an a automatically gets full width.

Responsive: On tablets, bases are divided by 1.5 (i.e. a 12-wide becomes 8-wide) and on mobile, by 3 (i.e. 12-wide becomes 4-wide) This elegantly accounts for the reduced widths, making column counts function as minimum absolute widths rather than as a percentage of the viewport.

Example: grid of 12ths vp-grid="12"

1
vp-cols="11"
vp-cols="2"
vp-cols="10"
vp-cols="3"
vp-cols="9"
vp-cols="3"
vp-cols="3"
vp-cols="3"
vp-cols="3"
vp-cols="4"
vp-cols="8"
vp-cols="4"
vp-cols="4"
vp-cols="4"
vp-cols="5"
vp-cols="7"
vp-cols="6"
vp-cols="6"
vp-cols="12"
vp-cols="3"
vp-cols="5"

Stacks & Shelves .vp-stack, .vp-shelf

Applicable to any container block, including body. Sensitive to density.

.vp-stack
2 or 3 immediate children are stacked vertically. The second child expands as necessary to make sure that the optional third child is always no higher than the bottom of the viewport.
.vp-shelf
Same behavior, but horizontal from left to right. Set width using min-width to help with responsiveness. Two-column shelves have a Fibonacci-derived default ratio of (38.2%,61.8%), three-column shelves have (23.6%,52.8%,23.6%). Responsive: On tablets down, the third child goes below the other two and on mobile all three children become vertically stacked.

Examples:

header
left

main

footer
header
left

main

...

...

...

...

...

right
footer
header (sticky)
left

main

...

...

...

...

...

right
footer (sticky)

Options

[vp-grid].stacking
Makes grid cells immediately go full-width for "tablet" and "mobile" sizes.
[vp-grid].growing
On desktop, fills incomplete rows by expanding each element in proportion to each other. On tablet-down, this is always the case.
[vp-grid].justified
On desktop, adds white space between elements in incomplete rows, to left and right align. On tablet-down, elements grow instead.
[vp-grid].centered
On desktop, adds white space left, right and between elements evenly in incomplete rows. On tablet-down, elements grow instead.

Forms forms.css

Note also that margins between some elements is affected by the form's density.

Fields label.vp-field > input|select|textarea

In addition to text, typical input types compatible with this class are: email, number, password, tel, text, url and possibly search. Fields expand to fill their container's width, which is handy in grids. We implement infield top aligned labels.

With JS enabled, an input without a forced width may grow/shrink with its content with the .vp-growing class. For textarea, height changes instead. Add .has-warning to a .vp-field or .invalid to a field or input/select/textarea for a permanent invalid indicator. (Though if JS is enabled, .invalid will be removed upon an edit which passes the browser's validity check.)
or plain:

Fieldset of fields and buttons
This is a multi-line warning explaining why this form field is inadequate in its current state.

Checkboxes label input[type=checkbox]

Radio selections label input[type=radio]

General rules of thumb: if there are between 3 and 5-7 options, a radio is probably the best input. Stack vertically if possible; space generously if horizontal. For 2 options, a checkbox is clearer. For more than 5-7 options, a select drop-down keeps the form cleaner. Remember to always check a default option when presenting a form's initial state.

Buttons button, .vp-button

Note that button-looking anchors among actual buttons need tabindex="0" to be keyboard accessible.

Anchor

Button and field groups .vp-group > :is(button, .vp-button, input, select, textarea, .vp-field)

Optional .vp-group.growing makes any input grow proportionally to fill the container space. Note that hidden members on either edge will make the corners on that edge sharp instead of rounded, so prefer disabling when possible. When including a .vp-field which is split (i.e. with prefix/postfix elements), add class .bare if it is unlabeled, to keep it thin like regular inputs/selects.

Theming

Normal buttons automatically adapt to any background/foreground colors and font sizes set on their elements, as well as their parent background color. Icon buttons adapt to font size and their parent background color.

Responsive

For touch devices, all buttons have a minimum width and height of 16mm regardless of font size.

Tables tables.css

Tables respond to density with padding and font size.

FirstSecondThirdFourth
First2.22%XFourth is longer as a test
First2.22%XFourth is longer as a test
First2.22%XFourth is longer as a test
First2.22%XFourth is longer as a test
First2.22%XFourth is longer as a test
SecondThirdFourth
First2.22%XFourth is longer as a test
First2.22%XFourth is longer as a test
First2.22%XFourth is longer as a test
First2.22%XFourth is longer as a test
First2.22%XFourth is longer as a test

Options

table.vp-2d
First column is a heading for each row, so hide first column of thead and tfoot.
table.horizontal
Horizontal borders only.
table.gridded
Horizontal and vertical borders.
table.striped
Highlights every even row in the tbody.
table.stacking
Cells grow to full width on mobile (portrait) screens. No border between cells, just between rows.
table.stick-vertical
thead and tfoot (all th) remain in viewport while the table is.
table.stick-horizontal
Left-most column (a th) is considered a header and remains in viewport while the table is.
[vp-center="..."]
Center text in all whitespace-delimited column numbers (1..9) of direct children of this element. Apply to tbody typically, or to table if you don't use it.
[vp-right="..."]
Right-align text in all whitespace-delimited column numbers (1..9) of direct children of this element. Apply to tbody typically, or to table if you don't use it.
[vp-mono="..."]
Use monospace font in all whitespace-delimited column numbers (1..9) of direct children of this element. Apply to tbody typically, or to table if you don't use it.

Notes notes.css

Element vp-note

This note has class="error". This note has class="warning". This note has class="ok". This note has no semantic status class. This note has class="boxed error". This note has class="boxed bg warning". This note has class="boxed bg ok". This note has class="boxed" and vp-point="top-left".

Options

.boxed
Not only adds a thin border, but also expands padding.
.bg
Adds a background at 5% opacity.
[vp-point="..."]
Adds a pointer triangle to any block, but designed to work with vp-note. The value is a string composed of two words separated by a hyphen, e.g. top-left. The first word specifies which border it applies to, one of: top, bottom, left or right. The second word specifies where on the border, either one of left, center, right or top, middle, bottom.

Tabs tabs.css tabs.js

Accordions

A series of details elements, each with a summary child before the rest of their content, can perform as an accordion if each details is given an identical name="..." attribute.

This framework doesn't yet include styles for this basic HTML5 construct, but it is planned for the future.

Tabs vp-tabs

A block which contains two children: a list of tab handles (inline-block), and a list of tab panels (block). A little bit of JS adds the necessary ARIA attributes and behavior, such as arrows, home/end and proper tab support.

Tab 1 Tab 2 Tab 3

This is tab 1.

This is tab 2.

It is taller than the others to demonstrate how height is dynamic.

This is tab 3.

Tab 1 Tab 2 Tab 3

This is tab 1.

This is tab 2.

It is taller than the others to demonstrate how height is dynamic.

This is tab 3.

Options

vp-tabs.boxed
Adds a box (similar to a fieldset) around the panel area.

Transitions transitions.css

To avoid "flashing", transitions only apply inside a .vp-loaded, which is applied automatically when the document is fully loaded by our main JS bundle.

Generic

.loaded, .loaded *
Subtle fade for background-color and padding.
.go-hidden, .go-visible
Fade in/out of opacity. Only useful in scripts because they are mutually exclusive.
TODO
Slide in/out from one of the 4 sides