Installation

Installing Mod in a Joystick App

How to install Mod for use in a Joystick app.

Mod is tightly integrated with Joystick, the full-stack JavaScript framework built by Mod's creator, CheatCode.

In order to integrate with Joystick, you only need to copy files from the mod.zip file you downloaded from the Mod App into your Joystick app. Behind the scenes, Joystick will automatically try to detect Mod, and if it's present, load its CSS and JS during server-side rendering (SSR).

Joystick will also handle user theme preferences automatically and gives you the ability to set a global default theme as a fallback.

Loading Mod's CSS, JavaScript, and Font

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_version.txt private/mod/mod_version.txt A file that tells Joystick which version of Mod your app is using (free or plus).
Folder components/ private/mod/components The CSS for individual Mod components. Used for tree-shaking Mod during server-side rendering (SSR).
Folder globals/ private/mod/globals Global CSS files for Mod. Used for tree-shaking Mod during server-side rendering (SSR).
File mod-light(-plus).min.css private/mod/mod-light(-plus).min.css The light-themed version of Mod.
File mod-dark(-plus).min.css private/mod/mod-dark(-plus).min.css The dark-themed version of Mod.
File mod(-plus).iife.min.js public/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 mod(-plus).esm.min.js lib/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've copied all of the required files into your app, the last step is to add Mod's font, Inter, to your app's index.html file:

index.html

<!doctype html>
<html class="no-js" lang="en">
  <head>
    <meta charset="utf-8">
    <title>App</title>
    <meta name="description" content="A new Joystick app.">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#FFCC00">
    <link rel="apple-touch-icon" href="/apple-touch-icon-152x152.png">
    <link rel="stylesheet" href="/_joystick/index.css">

    <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">
    
    <link rel="manifest" href="/manifest.json">
    ${css}
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

Above, we have three <link> tags from Google that handle pre-connection to their CDN and include the CSS for the font.

With all of that in place, Mod is ready to use.

Accessing Mod's JavaScript via components

If Mod is detected in your app (relative to the paths described in the table above), Joystick will automatically load the version of Mod's JavaScript into your app (based on the value inside of the private/mod_version.txt file—e.g., if this contains plus, the JavaScript for Mod Plus will be loaded).

Now, in your UI, all components will have access to Mod's JavaScript via the component instance at instance.mod.

import joystick from '@joystick.js/ui';

const Login = joystick.component({
  lifecycle: {
    on_mount: (instance = {}) => {
      instance.mod.password_input();
    },
  },
  render: () => {
    return `
      <form class="login">
        <div class="mod-form-input">
          <label class="mod-input-label">Email Address</label>
          <input type="email" name="email_address" class="mod-input" placeholder="Email Address" />
        </div>
        <div class="mod-form-input">
          <label class="mod-input-label">Password</label>
          <div class="mod-password-input-show-hide">
            <input type="password" class="mod-input" name="password" placeholder="Password" />
            <i class="mod-icon-eye"></i>
          </div>
        </div>
      </form>
    `;
  },
});

export default Login;

Above, to enable the interactive behavior on the mod-password-input element, inside of our lifecycle.on_mount() method, we access instance.mod.password_input() which attaches the JavaScript for the mod-password-input element.

Loading JavaScript Manually

Loading Mod's ESM JavaScript

While the above approach will work for most use cases, if you need to load Mod's JavaScript manually, you can import it from the lib folder at the root of your app like any other JavaScript module (assuming .esm.js variant usage).

Not Recommended

The following approach is not recommended as Joystick will still attempt to load Mod's JavaScript automatically in your app (i.e., doing the below will result in the same JavaScript being loaded for the functions you import on the client twice).

ui/pages/index/index.js

import joystick from '@joystick.js/ui-canary';
import { command_palette } from '../../../lib/mod(-plus).esm.min.js';

const Index = joystick.component({
  lifecycle: {
    on_mount: () => {
      command_palette();
    },
  },
  render: ({ props, state, data, methods }) => {
    return `
      <div class="mod-command-palette">
        <!-- HTML for Mod's command palette component -->
      </div>
    `;
  },
});

export default Index;

Up top, we add an import for the command_palette() method from Mod's JavaScript file. On our component, we define the on_mount lifecycle method (this runs as soon as our component is mounted on screen), and inside, call to the command_palette() method.

Loading Mod's IIFE JavaScript

Optionally (though not recommended), you can load the .iife.js variant of Mod's JavaScript in your app's index.html file at the root of your app.

Not Recommended

The following approach is not recommended as Joystick will still attempt to load Mod's JavaScript automatically in your app (i.e., doing the below will result in the same JavaScript being loaded on the client twice).

index.html

<!doctype html>
<html class="no-js" lang="en">
  <head>
    <meta charset="utf-8">
    <title>App</title>
    <meta name="description" content="A new Joystick app.">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#FFCC00">
    <link rel="apple-touch-icon" href="/apple-touch-icon-152x152.png">
    <link rel="stylesheet" href="/_joystick/index.css">

    <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">

    <script src="/mod(-plus).iife.min.js"></script>
    
    <link rel="manifest" href="/manifest.json">
    ${css}
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

Above, we've added a <script> tag pointing to Mod's IIFE JavaScript file that you copied over to the public folder in your app earlier. To access the JavaScript at a component-level, we can do the following:

ui/pages/index/index.js

import joystick from '@joystick.js/ui-canary';

const Index = joystick.component({
  lifecycle: {
    on_mount: () => {
      if (window.mod) {
        window.mod.command_palette();
      }
    },
  },
  render: ({ props, state, data, methods }) => {
    return `
      <div class="mod-command-palette">
        <!-- HTML for Mod's command palette component -->
      </div>
    `;
  },
});

export default Index;

Here, because we loaded Mod globally, we expect window.mod to be defined. On our component, we define the on_mount lifecycle method (this runs as soon as our component is mounted on screen), and inside, first check if window.mod is defined and if it is, call to our component's JavaScript method window.mod.command_palette().

Setting a Default Theme

By default, Joystick will default to Mod's light theme in your app. If you'd like to override this, in your index.server.js file, you can specify the default theme via your app's options:

index.server.js

import joystick from "@joystick.js/node-canary";

joystick.app({
  mod: {
    default_theme: 'dark',
  },
  routes: { ... },
});

Above, we set the mod object in our app options to an object with a default_theme key and a value equal to the Mod theme that we'd like to default our app to (here, dark).

By default, Joystick will use this theme for your app if the current user does not have a theme cookie defined in their browser (setting this cookie can be done wherever you'd like in your app's UI).

The preference order for which theme gets applied to your app is as follows:

  1. If the user has a theme cookie defined, use that.
  2. If there's no theme cookie, fall back to the mod.default_theme (what we've shown above) if it's defined.
  3. If there's no theme cookie and no mod.default_theme set, fall back to light.

Using Tree-Shaking

Experiemental: Use With Caution

This approach is best reserved for apps that need (or want) to critically reduce page size. Keep in mind that if you use this approach, parts of your UI may look broken if you haven't specified their component name in the mod.components array.

Using Mod's full CSS file can be helpful to speed up the development process, however, this can add unnecessary weight to pages as they're likely to use only a few of Mod's components.

Because your app can use conditional logic (in a Joystick app, HTML wrapped in a when() render method) that prevents Joystick from "seeing" all of the Mod components in use in your app, as an alternative, Joystick offers the ability to specify which component's to load CSS for at the route level:

index.server.js

import joystick from "@joystick.js/node-canary";

joystick.app({
  mod: {
    default_theme: 'dark',
  },
  routes: {
    '/': (req = {}, res = {}) => {
      res.render('ui/pages/index/index.js', {
        layout: 'ui/layouts/app/index.js',
        mod: {
          components: [
            'alert',
            'button',
            'command-palette',
            ...other components in use...
          ],
        },
      });
    },
  },
});

Above, for our hypothetical / index route, to the options object for our res.render() call, we've added a mod object with a components array containing the names of Mod components we'd like Joystick to load the CSS for. Any component used on the page that is not in this array will appear unstyled.