Skip to content

Instantly share code, notes, and snippets.

@pjlamb12
Last active July 11, 2023 22:23
Show Gist options
  • Save pjlamb12/2ad54d7f1ab20c6f7a88429f31c7f27b to your computer and use it in GitHub Desktop.
Save pjlamb12/2ad54d7f1ab20c6f7a88429f31c7f27b to your computer and use it in GitHub Desktop.
Tailwind Theme Loader Service
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { TailwindThemeModule } from './tailwind-theme';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
AppRoutingModule,
TailwindThemeModule.forRoot({ configUrl: './assets/config/tailwind-theme.config.json' }),
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
export class TailwindThemeConfig {
configUrl: string;
constructor(obj: any = {}) {
this.configUrl = obj.configUrl || './assets/tailwind-theme.config.json';
}
}
export interface Color {
name: string;
hex: string;
isDarkContrast: boolean;
}
export interface TailwindTheme {
'primary-color': string;
'secondary-color': string;
'accent-color': string;
}
import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TailwindThemeService } from './tailwind-theme/tailwind-theme.service';
import { TailwindThemeConfig } from './tailwind-theme-config.class';
export function initTailwindThemeConfig(tailwindThemeSvc: TailwindThemeService) {
return () => tailwindThemeSvc.loadConfig();
}
@NgModule({
imports: [CommonModule],
providers: [
TailwindThemeService,
{
provide: APP_INITIALIZER,
useFactory: initTailwindThemeConfig,
deps: [TailwindThemeService],
multi: true,
},
],
})
export class TailwindThemeModule {
static forRoot(config: TailwindThemeConfig): ModuleWithProviders<TailwindThemeModule> {
return {
ngModule: TailwindThemeModule,
providers: [
{
provide: TailwindThemeConfig,
useValue: config,
},
TailwindThemeService,
],
};
}
}
import { Inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DOCUMENT } from '@angular/common';
import { TailwindTheme, updateThemeVariables } from '../tailwind-util';
import { TailwindThemeConfig } from '../tailwind-theme-config.class';
import { switchMap } from 'rxjs/operators';
@Injectable()
export class TailwindThemeService {
constructor(
private _http: HttpClient,
@Inject(DOCUMENT) private readonly document: Document,
private config: TailwindThemeConfig,
) {}
loadConfig(): Promise<any> {
const configUrl = this.config.configUrl || './assets/tailwind-theme.config.js';
return this._http
.get(`${configUrl}`)
.pipe(
switchMap((configObject: { themeUrl: string }) => {
return this._http.get(configObject.themeUrl);
}),
)
.toPromise()
.then((themeData: TailwindTheme) => {
updateThemeVariables(themeData, this.document);
})
.catch((err: any) => {
console.error('There was an error while loading the Tailwind Theme.');
});
}
}
// Manually generate the same palette with https://www.tailwindshades.com/
import * as tinycolor from 'tinycolor2';
import { Color, TailwindTheme } from './tailwind-theme.interface';
export function computeColorPalette(hex: string): Color[] {
return [
getColorObject(tinycolor(hex).lighten(45), '50'),
getColorObject(tinycolor(hex).lighten(40), '100'),
getColorObject(tinycolor(hex).lighten(30), '200'),
getColorObject(tinycolor(hex).lighten(20), '300'),
getColorObject(tinycolor(hex).lighten(10), '400'),
getColorObject(tinycolor(hex), '500'),
getColorObject(tinycolor(hex).darken(10), '600'),
getColorObject(tinycolor(hex).darken(20), '700'),
getColorObject(tinycolor(hex).darken(30), '800'),
getColorObject(tinycolor(hex).darken(40), '900'),
];
}
export function getColorObject(value: tinycolor.Instance, name: string): Color {
const c = tinycolor(value);
return {
name,
hex: c.toHexString(),
isDarkContrast: c.isLight(),
};
}
export function updateThemeVariables(theme: TailwindTheme, document: Document) {
for (const [name, color] of Object.entries(theme)) {
const palette = computeColorPalette(color);
for (const variant of palette) {
document.documentElement.style.setProperty(`--${name}-${variant.name}`, variant.hex);
}
}
}
module.exports = {
future: {
// removeDeprecatedGapUtilities: true,
// purgeLayersByDefault: true,
// defaultLineHeights: true,
// standardFontWeights: true
},
purge: ['./apps/**/src/**/*.html', './apps/**/**/*.ts', './libs/**/*.html', './libs/**/*.ts'],
theme: {
extend: {
colors: {
'primary-color': {
DEFAULT: 'var(--primary-color)',
50: 'var(--primary-color-50)',
100: 'var(--primary-color-100)',
200: 'var(--primary-color-200)',
300: 'var(--primary-color-300)',
400: 'var(--primary-color-400)',
500: 'var(--primary-color-500)',
600: 'var(--primary-color-600)',
700: 'var(--primary-color-700)',
800: 'var(--primary-color-800)',
900: 'var(--primary-color-900)',
},
'secondary-color': {
DEFAULT: 'var(--secondary-color)',
50: 'var(--secondary-color-50)',
100: 'var(--secondary-color-100)',
200: 'var(--secondary-color-200)',
300: 'var(--secondary-color-300)',
400: 'var(--secondary-color-400)',
500: 'var(--secondary-color-500)',
600: 'var(--secondary-color-600)',
700: 'var(--secondary-color-700)',
800: 'var(--secondary-color-800)',
900: 'var(--secondary-color-900)',
},
'accent-color': {
DEFAULT: 'var(--accent-color)',
50: 'var(--accent-color-50)',
100: 'var(--accent-color-100)',
200: 'var(--accent-color-200)',
300: 'var(--accent-color-300)',
400: 'var(--accent-color-400)',
500: 'var(--accent-color-500)',
600: 'var(--accent-color-600)',
700: 'var(--accent-color-700)',
800: 'var(--accent-color-800)',
900: 'var(--accent-color-900)',
},
},
},
},
variants: {},
plugins: [],
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment