Sol Tokens

Modern Sol package artifacts wired into Rails. Shared tokens, fonts, Tailwind v4 adapter utilities, and session color runtime running against real collection data.

View as slides →

Typography

MoMA Sans across six weights. Sol's atom system toggles regular (400) and bold (900). The other four are available via @font-face declarations.

Extra Light (100)
The Museum of Modern Art
Light (300)
The Museum of Modern Art
Regular (400)
The Museum of Modern Art
Medium (600)
The Museum of Modern Art
Semibold (700)
The Museum of Modern Art
Bold (900)
The Museum of Modern Art
small
160,000 artworks in semantic space
medium
160,000 artworks in semantic space
large
160,000 artworks in semantic space
medium-large
160,000 artworks
xl
160,000 artworks
xxl
160,000
hero
MoMA

This page now loads the modern Sol core package through the Rails asset pipeline. The type scale, font faces, cap-height metrics, and baseline trimming all come from the shared package stylesheet.

sol/core.css — Typography
/* Font sizes: mobile defaults, desktop overrides at 768px */
:root {
  --typography--size--small:  1.4rem;
  --typography--size--medium: 1.6rem;
  --typography--size--large:  2.0rem;
  --typography--size--xl:     2.8rem;
  --typography--size--hero:   3.6rem;

  /* Cap height metrics (inherent to MoMA Sans) */
  --typography--cap-height: 0.75;

  /* Sol's baseline leading: 4/3 */
  --typography--leading--body: 1.333;
}

/* Desktop (Sol's $desktop breakpoint) */
@media (min-width: 768px) {
  :root {
    --typography--size--hero: 8.0rem;
  }
}

The principle: Sol's extraordinary typographic precision — cap-height calculations, baseline-aligned leading, responsive scaling — lives in the values, not the delivery mechanism. Extract the values; the precision travels.

Session Color

12 brand colors. 7 are session-eligible (full opacity below). Each visitor gets one randomly on first visit. Click a session color to preview.

Session color is now app-wide, not demo-local. A shared Stimulus controller persists the chosen key to the current Rails user and updates <html> for both the main app shell and this page.

session_color_controller.js — Session Color
// Shared Sol runtime helpers pick the color number.
const colorNumber = getSessionColorNumber(colorKey)

// Variables are written once and bridged for legacy app selectors.
applySessionColorVariables({ rootElement: document.documentElement, colorNumber })
document.documentElement.style.setProperty("--session-color", `var(--color--brand--${colorNumber})`)

Integration detail: the shared controller keeps the legacy --session-color alias in sync so existing app surfaces and the modern Sol page can run side by side during migration.

Collection

Real artworks from MoMA's 160,000-work collection. Cards, typography, spacing, and accent colors all drawn from Sol's design tokens.

Decade
Department

Click a neighbor and it becomes the featured artwork — its own neighbors load with a fade transition. Decade and department pills filter the random selection. Three bands sample from different depths of the 32-neighbor range, with the featured artist excluded from Distant Echoes to guarantee escape velocity.

sol_demo_controller.js — Neighbor Bands
// Flatten 32 neighbors, sort by similarity, sample 3 depths.
// Very Similar: top 3 (same artist is accurate).
// Related: middle of range, 1 per artist.
// Distant Echoes: bottom, featured artist excluded.
const echoPool = all.filter(
  c => c.artist !== featuredArtist
)

// Click a neighbor → instantly swap featured, fade-load new neighbors
async focusNeighbor(event) {
  // 1. Instant: swap featured card from clicked card's data
  // 2. Fade out neighbor grid (200ms)
  // 3. Fetch new neighbors for clicked artwork
  // 4. Fade in
}
focus_item_query.rb — Filtered Random
# Decade + department filters on random selection
base = Artwork.where.not(image_url: [nil, ""])
base = base.by_department(department) if department
base = base.where("date ~ ?", "\\m#{decade}\\d\\M") if decade
base.order("RANDOM()").limit(1).pick(:id)

The pattern: Backend filters with standard ActiveRecord scopes. Frontend manages state transitions — fade out, swap, wait for image load, fade in. Stimulus actions on every control; one loadArtwork() method rebuilds the query string from current filter state.

Semantic Axis

Type a concept and the collection arranges itself along the spectrum from the featured artwork toward your idea. 160,000 works projected onto a line through meaning-space.

Pole A (loading artwork...)
Pole B (enter a concept)

The axis projects 160,000 artworks onto a line between two poles: a specific artwork and an abstract concept. Each work gets a position (0–100%) based on its proximity to each pole in 1,536-dimensional embedding space.

sol_demo_controller.js — Axis Projection
// The endpoint does the heavy lifting:
// embed the concept, compute axis, project all works.
const url = `/explore/axis?id_a=${id}
  &q_b=${encodeURIComponent(concept)}`

// Visualization: stem height encodes distance from center.
// Taller stems = closer to poles = stronger signal.
const stemHeight = 2 + Math.abs(pos - 0.5) * 8

What this demonstrates: Design tokens and collection data aren't separate concerns. The same --color--session that tints the card borders tints the axis gradient. The same sol-card component renders the axis thumbnails. The design system becomes a lens for exploring the collection.

Notes

Modern Sol now runs inside moma-browser as a shared Rails-served foundation rather than a standalone prototype.

  • Shared package artifacts power the page. Tokens, font faces, and baseline typography come from the modern sol/core.css asset imported into the Rails pipeline.
  • Session color is app-wide and persistent. A shared Stimulus controller writes --color--session and the legacy --session-color alias on <html> while persisting the selected key to the current Rails user.
  • Importmap, Stimulus, and Tailwind v4 replace the old tooling. Sol runtime helpers load as ES modules, and the Tailwind adapter projects Sol tokens into utility classes without Webpack or Sass macros.
  • Museum entity contracts stay shared. A shared as_browser_json interface across Artwork, Exhibition, Constituent, and ArtTerm lets one card contract render all four types.