Installation

Installing Mod in a Ruby on Rails App

How to install Mod for use in a Ruby on Rails app.

To use Mod with Ruby on Rails, you will want to decide how you want Mod's files loaded in your app. There are two options:

Global

This will load Mod's CSS and JavaScript globally in your app for all pages. If you're not an advanced Ruby on Rails developer, this is the recommended approach.

Global CSS and ESM JavaScript

This will load Mod's CSS globally and set up your JavaScript to load via ESM. This is recommended for advanced Ruby on Rails developers building a production-level app that want to reduce page size.

Using Global CSS and JavaScript

To start installing Mod in your app, first, you'll want to copy the required Mod files into your app. Use the table below to know where files should go:

Paths for Plus Users

If you've purchased a copy of Mod Plus, you will want to use the plus file variants (e.g., mod-light-plus.min.css) below.

Type File in mod(-plus).zip Target path in app What is it?
File mod-light(-plus).min.css app/assets/stylesheets/mod-light(-plus).min.css The light-themed version of Mod.
File mod-dark(-plus).min.css app/assets/stylesheets/mod-dark(-plus).min.css The dark-themed version of Mod.
File mod(-plus).iife.min.js app/assets/javascript/mod(-plus).iife.min.js The IIFE version of the JavaScript for Mod's interactive components. Use this if you want to load Mod's JavaScript globally in your app.
File fonts/lucide.woff2 public/fonts/lucide.woff2 The icon font used by Mod, Lucide.
File fonts/mod-brand-icons.woff2 public/fonts/mod-brand-icons.woff2 The icon font for brand logos used by Mod.

Once you have the files copied over to your app, next, you'll want to update your config/initializers/assets.rb file to include a precompile step for the CSS and JavaScript files:

config/initializers/assets.rb

# If using Mod Plus, remove the parentheses around -plus below. If using Mod's free version,
# remove the (-plus) from each line.

Rails.application.config.assets.precompile += %w[
  mod-light(-plus).min.css
  mod-dark(-plus).min.css
  mod(-plus).iife.min.js
]

With that in place, the last thing to update is your main HTML file at app/views/layouts/application.html.erb:

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title><%= content_for(:title) || "Mod" %></title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="mobile-web-app-capable" content="yes">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= yield :head %>

    <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
    <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>

    <link rel="icon" href="/icon.png" type="image/png">
    <link rel="icon" href="/icon.svg" type="image/svg+xml">
    <link rel="apple-touch-icon" href="/icon.png">

    <!-- Start Mod Includes -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap" rel="stylesheet">
    
    <% theme = cookies[:theme] || 'light' %>
    <%= stylesheet_link_tag "mod(-plus)-#{theme}.min" %>
    <%= javascript_include_tag "mod(-plus).iife.min" %>
    <!-- End Mod Includes -->

    <!-- Avoid the include all and use a single tag for your application.css file -->
    <%= stylesheet_link_tag "application" %>

    <%# Includes all stylesheet files in app/assets/stylesheets %>
    <%#= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
  </head>

  <body data-mod-theme="<%= theme %>">
    <%= yield %>
  </body>
</html>

Above, you'll want to be mindful of placement. Make note of the <!-- Start Mod Includes --> and <!-- End Mod Includes --> comments. All of the required includes for the files we copied over earlier are included here. This also includes the Google Fonts CDN links for Mod's font, Inter (optional and can be replaced with static assets in the app).

For theme switching, we've created a variable theme, which stores the global cookies[:theme] value, or, a fallback to a default light if that cookie isn't set.

On our HTML's <body> tag, we're adding the data attribute data-mod-theme and passing it a reference to the theme variable we defined above.

data-mod-theme is required

Styles in Mod are theme-scoped using the data-mod-theme attribute on your <body> tag. If this is omitted, Mod's components will appear broken/unstyled.

With this, Mod should be loaded globally in your app. For pages with components that require JavaScript, calls to the specific component's JavaScript can be loaded directly in your view template's HTML:

app/views/pages/test.html.erb

<div class="mod-command-palette">
  <!-- HTML for command palette component... -->
</div>

<script>
  document.addEventListener('DOMContentLoaded', () => {
    if (window.mod) {
      window.mod.command_palette();
    }
  })
</script>

Above, we add a <script> tag to our template and inside, add an event listener for the DOMContentLoaded event. In that listener's callback, we check to see if the global window.mod value is defined, and if it is, call to the JavaScript method to enable interaction for the component(s) on our page (here, we're using the Mod Plus Command Palette component).

Using Global CSS and ESM JavaScript

To start installing Mod in your app, first, you'll want to copy the required Mod files into your app. Use the table below to know where files should go:

Paths for Plus Users

If you've purchased a copy of Mod Plus, you will want to use the plus file variants (e.g., mod-light-plus.min.css) below.

Type File in mod(-plus).zip Target path in app What is it?
File mod-light(-plus).min.css app/assets/stylesheets/mod-light(-plus).min.css The light-themed version of Mod.
File mod-dark(-plus).min.css app/assets/stylesheets/mod-dark(-plus).min.css The dark-themed version of Mod.
File mod(-plus).esm.min.js app/assets/javascript/mod(-plus).esm.min.js The ESM version of the JavaScript for Mod's interactive components. Use this if you want to load Mod's JavaScript incrementally in your app.
File fonts/lucide.woff2 public/fonts/lucide.woff2 The icon font used by Mod, Lucide.
File fonts/mod-brand-icons.woff2 public/fonts/mod-brand-icons.woff2 The icon font for brand logos used by Mod.

Once you have the files copied over to your app, next, you'll want to update your config/initializers/assets.rb file to include a precompile step for the CSS and JavaScript files:

config/initializers/assets.rb

# If using Mod Plus, remove the parentheses around -plus below. If using Mod's free version,
# remove the (-plus) from each line.

Rails.application.config.assets.precompile += %w[
  mod-light(-plus).min.css
  mod-dark(-plus).min.css
  mod(-plus).esm.min.js
]

Next, you'll also want to update your import map to include mapping for Mod's JavaScript file:

config/importmap.rb

# Pin npm packages by running ./bin/importmap

pin "application"
pin "@hotwired/turbo-rails", to: "turbo.min.js"
pin "@hotwired/stimulus", to: "stimulus.min.js"
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
pin_all_from "app/javascript/controllers", under: "controllers"

# Add this line at the bottom:
pin "mod(-plus)", to: "mod(-plus).esm.min.js"

Above, we've added the line pin "mod-plus", to: "mod-plus.esm.min.js" so that references to mod-plus in our JavaScript are properly mapped back to our JavaScript file.

With that in place, the last thing to update is your main HTML file at app/views/layouts/application.html.erb:

app/views/layouts/application.html.erb

<!DOCTYPE html>
<html>
  <head>
    <title><%= content_for(:title) || "Mod" %></title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="mobile-web-app-capable" content="yes">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= yield :head %>

    <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
    <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>

    <link rel="icon" href="/icon.png" type="image/png">
    <link rel="icon" href="/icon.svg" type="image/svg+xml">
    <link rel="apple-touch-icon" href="/icon.png">

    <!-- Start Mod Includes -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap" rel="stylesheet">
    
    <% theme = cookies[:theme] || 'light' %>
    <%= stylesheet_link_tag "mod(-plus)-#{theme}.min" %>
    <%= javascript_include_tag "mod(-plus).iife.min" %>
    <!-- End Mod Includes -->

    <!-- Avoid the include all and use a single tag for your application.css file -->
    <%= stylesheet_link_tag "application" %>

    <%# Includes all stylesheet files in app/assets/stylesheets %>
    <%#= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
    
    <!-- Include these so imports work as expected and page-level JavaScript is loaded -->
    <%= javascript_importmap_tags %>
    <%= yield :page_javascript %>
  </head>

  <body data-mod-theme="<%= theme %>">
    <%= yield %>
  </body>
</html>

Above, you'll want to be mindful of placement. Make note of the <!-- Start Mod Includes --> and <!-- End Mod Includes --> comments. All of the required includes for the files we copied over earlier are included here. This also includes the Google Fonts CDN links for Mod's font, Inter (optional and can be replaced with static assets in the app).

For our JavaScript, we've added two very important lines:

<%= javascript_importmap_tags %>
<%= yield :page_javascript %>

These two lines ensure that, first, the necessary JavaScript import tags are included for all pages, and second, that any in-page JavaScript is loaded (see below).

For theme switching, we've created a variable theme, which stores the global cookies[:theme] value, or, a fallback to a default light if that cookie isn't set.

On our HTML's <body> tag, we're adding the data attribute data-mod-theme and passing it a reference to the theme variable we defined above.

data-mod-theme is required

Styles in Mod are theme-scoped using the data-mod-theme attribute on your <body> tag. If this is omitted, Mod's components will appear broken/unstyled.

With this, Mod should be loaded globally in your app. For pages with components that require JavaScript, calls to the specific component's JavaScript can be loaded directly in your view template's HTML:

app/views/pages/test.html.erb

<div class="mod-command-palette">
  <!-- HTML for command palette component... -->
</div>

<% content_for :page_javascript do %>
  <script type="module">
    import { command_palette } from "mod(-plus)";
    command_palette();
  </script>
<% end %>

Above, we've added a content_for conditional with a <script> tag inside. On the script tag, because we're loading an ESM JavaScript file, we've set the type attribute to module. Inside of our <script> tag, we import the method we need to enable interaction for the component(s) on our page (here, we're using the Mod Plus Command Palette component).