Rails - Importmaps

How to install PhlexUI within a Rails app that uses Importmaps.

Tailwind plugins not compatible with importmaps
This means that animation using tailwind-animate plugin will not work. I am thinking of a new way to implement this, most likely with a stimulus controller for animations.

Creating a Rails app

1

Generate a new Rails application

In case you don't have a Rails application set up yet, let's start by generating one. The demo uses importmaps (Rails default). Read more about Importmaps in Rails here.

rails new CHANGE_TO_NAME_OF_APP --css=tailwind

Once that is created, navigate to the app

cd CHANGE_TO_NAME_OF_APP

Install the gem

1


Install Phlex

Run the following in the terminal to install phlex for Rails

bundle add phlex-rails

After the gem is installed, run the generator to create necessary files.

bin/rails generate phlex:install

Refer to the Phlex installation guide for Rails for more information.

2


Install PhlexUI gem

Run the following in the terminal to install the PhlexUI Component Library

bundle add phlex_ui

3

Include PhlexUI module

Include PhlexUI module in your application_component.rb file

class ApplicationComponent < Phlex::HTML
	include PhlexUI
end

Install JS

1


Install package

Run the following in the terminal to install PhlexUI JS package

bin/importmap pin phlex_ui

2

Import package

Import the package in your app/javascript/application.js file

import 'phlex_ui';

Install Styles

1


Install TailwindCSS

In this guide we have already installed TailwindCSS, however if you have not done this make sure to follow the TailwindCSS installation guide

2


Update Tailwind Configuration

Add the following to your tailwind.config.js file

// For importing tailwind styles from phlex_ui/phlex_ui_pro gem
const execSync = require('child_process').execSync;

// Import phlex_ui gem path (To make sure Tailwind loads classes used by phlex_ui gem)
const outputPhlexUI = execSync('bundle show phlex_ui', { encoding: 'utf-8' });
const phlex_ui_path = outputPhlexUI.trim() + '/**/*.rb';

const defaultTheme = require('tailwindcss/defaultTheme')

module.exports = {
  darkMode: ["class"],
  content: [
    './app/views/**/*.{erb,haml,html,slim,rb}',
    './app/helpers/**/*.rb',
    './app/assets/stylesheets/**/*.css',
    './app/javascript/**/*.js',
    phlex_ui_path
  ],
  theme: {
    container: {
      center: true,
      padding: "2rem",
      screens: {
        "2xl": "1400px",
      },
    },
    extend: {
      colors: {
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
        secondary: {
          DEFAULT: "hsl(var(--secondary))",
          foreground: "hsl(var(--secondary-foreground))",
        },
        destructive: {
          DEFAULT: "hsl(var(--destructive))",
          foreground: "hsl(var(--destructive-foreground))",
        },
        warning: {
          DEFAULT: "hsl(var(--warning))",
          foreground: "hsl(var(--warning-foreground))",
        },
        success: {
          DEFAULT: "hsl(var(--success))",
          foreground: "hsl(var(--success-foreground))",
        },
        muted: {
          DEFAULT: "hsl(var(--muted))",
          foreground: "hsl(var(--muted-foreground))",
        },
        accent: {
          DEFAULT: "hsl(var(--accent))",
          foreground: "hsl(var(--accent-foreground))",
        },
      },
      borderRadius: {
        lg: `var(--radius)`,
        md: `calc(var(--radius) - 2px)`,
        sm: "calc(var(--radius) - 4px)",
      },
      fontFamily: {
        sans: defaultTheme.fontFamily.sans,
      },
    },
  },
}

3

Add CSS variables

Add the following to your app/assets/stylesheets/application.tailwind.css file

@tailwind base;
@tailwind components;
@tailwind utilities;


@layer base {
  :root {
    --background: 0 0% 100%;
    --foreground: 0 0% 3.9%;
    --primary: 0 0% 9%;
    --primary-foreground: 0 0% 98%;
    --secondary: 0 0% 96.1%;
    --secondary-foreground: 0 0% 9%;
    --muted: 0 0% 96.1%;
    --muted-foreground: 0 0% 45.1%;
    --accent: 0 0% 96.1%;
    --accent-foreground: 0 0% 9%;
    --destructive: 350 89% 60%;
    --destructive-foreground: 0 0% 100%;
    --warning: 38 92% 50%;
    --warning-foreground: 0 0% 100%;
    --success: 87 100% 37%;
    --success-foreground: 0 0% 100%;
    --border: 0 0% 89.8%;
    --input: 0 0% 89.8%;
    --ring: 0 0% 3.9%;
    --radius: 0.5rem;
  }

  .dark {
    --background: 0 0% 3.9%;
    --foreground: 0 0% 98%;
    --primary: 0 0% 98%;
    --primary-foreground: 0 0% 9%;
    --secondary: 0 0% 14.9%;
    --secondary-foreground: 0 0% 98%;
    --muted: 0 0% 14.9%;
    --muted-foreground: 0 0% 63.9%;
    --accent: 0 0% 14.9%;
    --accent-foreground: 0 0% 98%;
    --destructive: 350 89% 60%;
    --destructive-foreground: 0 0% 100%;
    --warning: 38 92% 50%;
    --warning-foreground: 0 0% 100%;
    --success: 84 81% 44%;
    --success-foreground: 0 0% 100%;
    --border: 0 0% 14.9%;
    --input: 0 0% 14.9%;
    --ring: 0 0% 83.1%;
  }
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply bg-background text-foreground;
    font-feature-settings: "rlig" 1, "calt" 1;
  }
}