Theming


Core Concepts

In this section, we will talk about Vactory Next core concepts such as fonts, PostCSS, Dark mode, RTL, Icons, etc.

Fonts

⚠️
Don't use SELF HOSTED fonts. Self-hosted fonts require a license, which developers might forget to obtain, potentially causing legal issues. You can only use fonts from Google Fonts & Monotype

You can configure your custom fonts either by using next/font/google in your _app.jsx file:

_app.jsx

import { Inter, Cairo } from "next/font/google"
const inter = Inter({
weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"],
display: "swap",
subsets: ["latin"],
})
export default function App({ Component, pageProps }) {
return (
<>
<style jsx global>{`
:root {
--sans-font-family: ${inter.style.fontFamily};
}
`}</style>
// ...
// ...
// Rest of code
<>
)
}

Or by using the <link> HTML tag in your _document.jsx file's Head:

_document.jsx

<Head>
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap"
rel="stylesheet"
/>
</Head>

PostCSS

When using Tailwind CSS with Next.js, PostCSS plays a crucial role. Next.js has built-in support for PostCSS, allowing you to easily configure and apply transformations to your CSS. By setting up PostCSS in Next.js, you can leverage plugins such as Autoprefixer (to add vendor prefixes).

This an example of our postcss.config.js file:

postcss.config.js

module.exports = {
plugins: [
// The 'tailwindcss/nesting' plugin enables the use of nested CSS selectors, similar to Sass or Less syntax.
"tailwindcss/nesting",
// The 'tailwindcss' plugin is the core plugin for integrating Tailwind CSS
"tailwindcss",
// The postcss-rtlcss plugin is used for transforming CSS from left-to-right (LTR) to right-to-left (RTL) direction.
// The configuration object { mode: "combined", useCalc: true } specifies the mode of transformation as "combined" and enables the use of calc function in the RTL styles.
["postcss-rtlcss", { mode: "combined", useCalc: true }],
// The autoprefixer plugin automatically adds vendor prefixes to CSS properties, ensuring cross-browser compatibility.
"autoprefixer",
],
}

Dark Mode

Tailwind includes a dark variant that lets you style your site differently when dark mode is enabled. For example, you can display a blue background-color when the device theme is set to light, and a red background when the device's theme is set to dark as shown below.

<div class="bg-blue dark:bg-red">
<h1>Dark mode</h1>
</div>

By default this uses the prefers-color-scheme CSS media feature, but you can also build sites that support toggling dark mode manually using the ‘class’ strategy.

RTL/LTR Support

Use the rtl and ltr modifiers to conditionally add styles in right-to-left and left-to-right modes respectively when building multi-directional layouts.

We have already configured RTL with PostCSS, which automatically switches classes that contain "left," "right," "top," or "bottom" to their opposites. For example, right-2 becomes left-2

Icons

To add a new icon to your project, follow these steps:

  • Place the icon in the YOUR_WORKSPACE/icons folder
  • Run the command yarn workspace YOUR_WORKSPACE icons:build
  • This process will add the icon to the YOUR_WORKSPACE/public/icons.svg file
  • You can access the icon by using the SVG ID, which corresponds to the name you assigned to your icon file:
import { Icon } from "@/ui"
// if your SVG file is called 'arrow-left.svg', the you can use it this way:
;<Icon id="arrow-left" width="16px" height="16px" />

Components folder

In this section, we will focus on the components folder, which contains the following subfolders: elements, widgets, modules, and global.

Most of the theming occurs in the files inside these folders. Let's examine each one of them, understand their purpose, and learn how to theme them:

I. Elements:

This folder contains reusable UI components that represent basic building blocks of your design. These components are typically styled at a low level and can be composed together to create more complex components.

For a better understanding, let's not just explain an existing element; instead, we will create a new element together and theme it. This hands-on approach will provide a practical experience in creating and customizing a component.

  1. Create a new component: Inside the elements folder, let's create a new file called Button.jsx. This file will contain the code for our custom button component.

  2. Define the component: In Button.jsx, define a functional React component for our button ( Don't use class components because it's deprecated )

button/Button.jsx

export const Button = ({ children }) => {
return (
<div>
<button>{children}</button>
</div>
)
}
  1. Style the component: Now, let's style our custom button. Usually, you might do this by creating a custom CSS file or using Tailwind classes directly inside your component. However, in our case, we are going to use a different approach which allows us to create multiple variants for a single component. Let's dive deeper into this:
  • Create a theme.js file: Inside the button folder, create a new file called theme.js. This file will hold our custom button styles and variants.

  • Define button variants: In theme.js, define different variants for our custom button. For example, let's create a primary variant:

button/theme.js

export const buttonVariants = {
primary: {
wrapper: "flex items-center",
element: "bg-primary-500 p-4 text-white",
},
secondary: {
wrapper: "flex items-center",
element: "bg-secondary-500 p-2 text-black",
},
}
  • Update the component: Now, update the Button.jsx component to utilize the variant styles defined in theme.js. Modify the component code as follows:

button/Button.jsx

import { buttonVariants } from "./theme"
export const Button = ({ variant = "primary", children }) => {
return (
<div className={buttonVariants[variant].wrapper}>
<button className={buttonVariants[variant].element}>{children}</button>
</div>
)
}

In the updated component, we added a variant prop that allows you to specify the desired button variant. By default, it is set to primary.

Now the structure of your folder will look like somthing like this:

apps
|------ YOUR_WORKSPACE
|------|------ components
|------|------|------ elements
|------|------|------|------ button
|------|------|------|------|------ Button.jsx
|------|------|------|------|------ theme.js
  1. Exporting the Element: Now you have to export your Button element and make it avalaible using Path Aliases, simply just add the following line of code to components/elements/index.js :
export { Button } from "./button/Button"

This line exports the Button component from the Button file, allowing you to import and use it using path aliases.

  1. Using the component: You can now use the Button component with different variants in your application. For example:
import React from "react"
import Button from "@/ui"
const App = () => {
return (
<>
<h1>Welcome to Vactory Next</h1>
<Button variant="primary">Primary Button</Button>
<Button variant="secondary">Secondary Button</Button>
</>
)
}
export default App

II. Widgets:

The widgets folder includes self-contained UI components that provide specific functionality or serve a particular purpose. These components are often more complex and combine multiple elements and have their own internal logic. To theme a widget, you can customize its appearance, behavior, and any specific options or settings it provides.

Check this section to learn more about Widgets: Static templates

III. Modules:

The modules folder contains larger, feature-rich components that combine multiple elements to create complete sections or functionality such as Listings. Theming modules involves customizing their styles, layout, and interactions to match your project's design and requirements.

Check this section to learn more about Modules: Dynamic templates

IV. Global:

The global folder houses global component which aren't Widgets nor Modules and configurations that apply across your entire application. This may include Error pages, Progress bar, etc.