GithubHelp home page GithubHelp logo

Comments (5)

alexanderbnelson avatar alexanderbnelson commented on May 24, 2024 1

I have theming implemented in my app. It's a multi-faceted approach that involves a combination of dynamically importing css and objects with tailwind classes.

Essentially all you need to do is get your subdomain or domain from next/headers at the root layout. Then use that to dynamically load your required theming info.

export default async function RootLayout(props: LayoutProps) {
  
  const headersList = headers();
  const host = headersList.get('host');
  const site = await getSiteData(host);
  
  ...

In my case getSiteData() looks like this - I have my hostname parsing function here

// Extract subdomain helper
function extractSubdomain(domain: string) {

  if (domain.endsWith(`.${process.env.NEXT_PUBLIC_ROOT_DOMAIN}`)) {
    return domain.replace(`.${process.env.NEXT_PUBLIC_ROOT_DOMAIN}`, '');
  } else if (domain.endsWith('.localhost:3000')) {
    return domain.replace('.localhost:3000', '');
  }

  return null;
}

export async function getSiteData(domain: string | null) {
  if (!domain) {
    return console.log("No domain provided");;
  }

  const subdomain = extractSubdomain(domain);

  return await unstable_cache(
    async () => {
      return prisma.site.findUnique({
        where: subdomain ? { subdomain: subdomain } : { customDomain: domain },
        include: {
          user: true,
          activeTheme: true,
        },
      });
    },
    [`${domain}-metadata`],
    {
      revalidate: 900,
      tags: [`${domain}-metadata`],
    },
  )();
}

Colour and font tokens are set by CSS variables and called for at the root with a data attribute. Those CSS variables are also set into my tailwind.config, just like @rpfelgueiras has shown in the link he referenced.

Set your data attributes on your tag. This is where you want font definitions especially

<html
      { "data-color-theme": `theme-${themeName}-color` }
      { "data-font-theme": `theme-${themeName}-font` }
    >

Define CSS variables

@layer base {

  [data-color-theme='theme-themeName-color'] { 
    --background: 0 0% 99%;
    --foreground: 45 25% 5%;

    --primary: 45 25% 5%;
    --primary-foreground: 45 25% 98%;
    ...

Then, tailwind classes are put into a theme-{themeName}.tsx file and applied in the site template code after dynamically importing the tailwind classes

Theme file

const themeName: Theme = {
    name: "The Theme Name",
    global: {
      text: "text-primary font-light",
      buttonRadius: "rounded-full"
    },
    ...

Dynamic import. This depends on naming your files with the same values you're using in your database to identify a site's theme.

const themeData = await import(`@/styles/themes/theme-${themeName}/theme-${themeName}`);`

Applied in components

<div className={cn(theme.profile.container)}>
        <p className={cn(theme.profile.name)}>{data?.displayName}</p>
        <div className={cn(theme.profile.infoContainer)}>
          ...

In my case, I'm not swapping out large chunks of JSX (yet). I'm just changing style. But there's no stopping you from having entire components conditionally render this way. It's a bit tedious to set up initially. But once the architecture is done, creating new themes is a bit less work.

from platforms.

chrishoermann avatar chrishoermann commented on May 24, 2024

I currently have the same requirement and had no luck with radix-theme yet - see #379

from platforms.

rpfelgueiras avatar rpfelgueiras commented on May 24, 2024

@chrishoermann I was searching for a solution and I will probably go in this direction tailwindlabs/tailwindcss#11964

In build time generate the right theme configurations per tenant and place the output in the right server/folder.

from platforms.

tomgreeEn avatar tomgreeEn commented on May 24, 2024

I have a ServerSideThemeWrapper wrapping everything in the [domain] route, which has access to the relevant Site, from which I pass the theme props.

Using shadcn/ui:


export function ServersideThemeWrapper({
  children,
  className,
  color,
  radius,
}: ServersideThemeWrapperProps) {
  return (
    <div
      className={cn(`theme-${color}`, "w-full", className)}
      style={
        {
          "--radius": `${radius ? radius : 0.5}rem`,
        } as React.CSSProperties
      }
    >
      {children}
    </div>
  );
}

from platforms.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.