Keep Rails Design Tokens Reviewable with rails-design-profiles

ruby dev.to

Design references are useful until they start changing production code by implication.

An AI assistant or a contributor can read a DESIGN.md file and understand a visual direction. That does not mean the application should execute the file, copy an entire stylesheet, or silently adopt a third-party brand. The safer boundary is to review the reference, approve a small set of values, and commit those values as an explicit application contract.

This tutorial shows that workflow with rails-design-profiles, a MIT-licensed Ruby gem for Rails. The stable v0.1.2 release stores profiles in YAML and emits the active profile as --rdp-* CSS custom properties.

TL;DR

Install the gem, run its generator, add a profile to config/design_profiles.yml, and include <%= rails_design_profiles_tags %> in your layout. Rails then emits a small style tag containing only valid token names and safe token values. Your existing CSS decides how to use those variables.

Prerequisites

You need:

  • Ruby 3.1 or newer.
  • Rails 7.1 or newer.
  • A Rails application where you can edit the Gemfile and application layout.
  • A CSS setup that can consume CSS custom properties.

The gem declares actionview >= 7.1 and railties >= 7.1 as dependencies. Its gemspec identifies the release as version 0.1.2 and the license as MIT.

Install the stable release

Add the gem to your Rails application's Gemfile:

gem "rails-design-profiles", "0.1.2"
Enter fullscreen mode Exit fullscreen mode

Install dependencies and run the generator:

bundle install
bin/rails generate rails_design_profiles:install
Enter fullscreen mode Exit fullscreen mode

The generator creates config/design_profiles.yml and adds this helper to the application layout's <head>:

<%= rails_design_profiles_tags %>
Enter fullscreen mode Exit fullscreen mode

The helper is intentionally small. It reads the active profile, converts accepted tokens into CSS custom properties, and renders one style tag. It does not replace your stylesheet or select a CSS framework.

Define an explicit profile

Open config/design_profiles.yml. The documented shape is a selected profile plus a map of named profiles:

active: editorial
profiles:
  editorial:
    reference: config/design_profiles/references/claude.md
    tokens:
      color-primary: "#c15f3c"
      color-surface: "#f8f6f2"
      color-text: "#24211e"
      font-body: "Newsreader,Georgia,serif"
      space-page: "1.5rem"
Enter fullscreen mode Exit fullscreen mode

The reference field documents where the human-readable context came from. It is metadata for people and tools. The tokens map is the production contract.

Use those variables from the CSS already in your application:

body {
  color: var(--rdp-color-text);
  background: var(--rdp-color-surface);
  font-family: var(--rdp-font-body);
}

.button {
  background: var(--rdp-color-primary);
  padding: var(--rdp-space-page);
}
Enter fullscreen mode Exit fullscreen mode

The gem changes a token name such as color-primary into --rdp-color-primary. The prefix keeps these values easy to locate and reduces accidental collisions with unrelated custom properties.

Import a reference without making it executable

Version 0.1 supports the MIT-licensed VoltAgent/awesome-design-md catalog. List its available references:

bin/rails design:list
Enter fullscreen mode Exit fullscreen mode

Install a reference into your application:

bin/rails design:install[claude]
Enter fullscreen mode Exit fullscreen mode

The task archives the reference at config/design_profiles/references/claude.md and creates an empty claude profile. Read it, discuss which ideas fit your product, and add only the tokens your team approves. Existing references and profiles are not overwritten.

This is an important security and maintenance boundary. The catalog file is content to inspect, not Ruby code to load. The gem downloads it over HTTPS, checks the response status, validates the slug shape, and writes it under the application's configuration directory.

The project also states that it is not affiliated with getdesign.md, VoltAgent, or any referenced brand. Treat an imported reference as an input to review, not as an endorsement or a ready-made theme.

Switch the active profile

After defining another profile, activate it explicitly:

bin/rails design:activate[editorial]
Enter fullscreen mode Exit fullscreen mode

The task updates the active key in config/design_profiles.yml. The new application-wide profile is used on the next response. Because the change is a normal configuration diff, a reviewer can see which values changed before deployment.

Per-session previews and query-string overrides are intentionally outside the first release. If you need a live theme picker, build that as a separate feature with its own authorization and caching design.

Verify the result

There are two useful checks. First, inspect the rendered HTML in a development response. You should find a style tag similar to this:

<style id="rails-design-profiles">:root {--rdp-color-primary: #c15f3c;--rdp-color-surface: #f8f6f2;}</style>
Enter fullscreen mode Exit fullscreen mode

Second, verify that the CSS uses the variables rather than hard-coded replacements. A browser's computed-style panel should show the value flowing from the custom property into the component.

The v0.1.2 test suite covers seven assertions across profile rendering, missing profiles, invalid YAML, reference installation, invalid slugs, activation, and helper output. In a clean checkout, the two Minitest files ran with 7 tests and 9 assertions, with zero failures or errors. The documented gem build also succeeded.

Why this works

The design decision is separation of concerns:

  1. DESIGN.md gives humans and AI tools context about a visual direction.
  2. A reviewed YAML profile records the subset that the product actually accepts.
  3. The helper exposes those values through standard CSS primitives.
  4. Existing components remain responsible for layout, interaction, and accessibility.

That separation makes changes reversible. Removing a token or changing the active profile is a small diff. It also avoids pretending that prose can safely compile an entire interface without review.

Failure modes and limitations

An unknown active profile raises ProfileNotFound. Invalid YAML raises ConfigurationError, as does a missing or malformed profiles mapping. A token is ignored unless its name uses lowercase letters, numbers, and hyphens, and its value matches the gem's conservative character allowlist. This filtering is helpful, but it is not a complete CSS sanitizer or a substitute for reviewing configuration changes.

The active profile is global to the Rails application and evaluated for each response. The first release does not provide user-specific themes, preview sessions, token validation diagnostics, or adapters for arbitrary public catalogs. The README lists those as planned work, so do not present them as current features.

The catalog download is a network dependency. Pin the gem version, review imported files, and commit the archived reference if reproducibility matters. Do not put secrets, user-provided CSS, or untrusted dynamic values into the profile. Keep the helper output under normal Rails content-security-policy and caching review.

FAQ

Does this replace Tailwind or the asset pipeline?

No. The gem emits CSS custom properties that can be consumed by the asset pipeline, Propshaft, Importmap, Tailwind, or custom CSS.

Does importing a reference install its branding?

No. The reference is archived as Markdown and the profile starts empty. Your team chooses the production tokens.

Can I override the profile with a query parameter?

Not in the first release. Profile switching is an explicit application-wide task.

Is the gem a design-system validator?

No. It provides a deliberately small bridge from reviewed configuration to CSS variables. The release roadmap mentions validation and diagnostics as future work.

Takeaway

rails-design-profiles is useful when you want design context to help Rails work without letting reference prose or copied CSS become an invisible production dependency. Keep the reference readable, keep the approved tokens in version control, and let your existing CSS consume the explicit --rdp-* contract.

What review rule would your team require before a design reference is allowed to influence production tokens?

Disclosure: AI assistance was used to organize and edit this tutorial. The project behavior, release metadata, source excerpts, and test results were checked against the public repository and stable v0.1.2 tag.

Source: dev.to

arrow_back Back to Tutorials