Skip to main content

Styling e Temi

Guida alla configurazione di stili e temi in Tess UI Library.

Setup Iniziale

PrimeNG (Raccomandato)

npm install primeng primeicons @primeuix/themes

angular.json:

{
"projects": {
"your-app": {
"architect": {
"build": {
"options": {
"styles": [
"node_modules/primeicons/primeicons.css",
"node_modules/@primeuix/themes/aura/aura-light-blue/theme.css",
"src/styles.scss"
]
}
}
}
}
}
}

Oppure in styles.scss:

@import "primeicons/primeicons.css";
@import "@primeuix/themes/aura/aura-light-blue/theme.css";

// Custom overrides
:root {
--primary-color: #3B82F6;
--surface-0: #ffffff;
}

Bootstrap

npm install bootstrap @ng-bootstrap/ng-bootstrap

styles.scss:

// Custom variables (prima dell'import)
$primary: #0d6efd;
$secondary: #6c757d;

@import "bootstrap/scss/bootstrap";

Material

npm install @angular/material @angular/cdk

styles.scss:

@import '@angular/material/prebuilt-themes/indigo-pink.css';

// O tema custom
@use '@angular/material' as mat;

$my-theme: mat.define-light-theme((
color: (
primary: mat.define-palette(mat.$indigo-palette),
accent: mat.define-palette(mat.$pink-palette),
)
));

@include mat.all-component-themes($my-theme);

Temi Dinamici

Multi-Theme con PrimeNG

angular.json:

{
"styles": [
"node_modules/primeicons/primeicons.css",
{
"input": "node_modules/@primeuix/themes/aura/aura-light-blue/theme.css",
"bundleName": "light",
"inject": false
},
{
"input": "node_modules/@primeuix/themes/aura/aura-dark-blue/theme.css",
"bundleName": "dark",
"inject": false
}
]
}

Componente:

import { Component, inject, OnInit } from '@angular/core';
import { ThemingService } from '@tess-ui-library/core';

@Component({
selector: 'app-root',
template: `
<button (click)="toggleTheme()">Toggle Theme</button>
<router-outlet></router-outlet>
`,
})
export class AppComponent implements OnInit {
private themingService = inject(ThemingService);

ngOnInit() {
this.loadTheme(this.themingService.getCurrentTheme());

this.themingService.currentTheme$.subscribe(theme => {
this.loadTheme(theme);
});
}

toggleTheme() {
const current = this.themingService.getCurrentTheme();
const newTheme = current === 'light' ? 'dark' : 'light';
this.themingService.setTheme(newTheme);
}

private loadTheme(theme: string) {
const themeLink = document.getElementById('theme-css') as HTMLLinkElement;

if (themeLink) {
themeLink.href = \`\${theme}.css\`;
} else {
const link = document.createElement('link');
link.id = 'theme-css';
link.rel = 'stylesheet';
link.href = \`\${theme}.css\`;
document.head.appendChild(link);
}
}
}

CSS Variables

Definizione

light-theme.scss:

:root[data-theme="light"] {
// Colors
--color-primary: #3B82F6;
--color-secondary: #6366F1;
--color-success: #10B981;
--color-warning: #F59E0B;
--color-danger: #EF4444;
--color-info: #3B82F6;

// Surfaces
--surface-ground: #F8F9FA;
--surface-section: #FFFFFF;
--surface-card: #FFFFFF;
--surface-overlay: #FFFFFF;
--surface-border: #DEE2E6;
--surface-hover: #E9ECEF;

// Text
--text-color: #212529;
--text-color-secondary: #6C757D;
--text-color-emphasis: #000000;

// Shadows
--shadow-1: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-2: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-3: 0 10px 15px rgba(0, 0, 0, 0.1);

// Spacing
--spacing-unit: 8px;

// Border radius
--border-radius-sm: 4px;
--border-radius: 8px;
--border-radius-lg: 12px;
}

dark-theme.scss:

:root[data-theme="dark"] {
--color-primary: #60A5FA;
--color-secondary: #818CF8;

--surface-ground: #121212;
--surface-section: #1E1E1E;
--surface-card: #1E1E1E;
--surface-overlay: #2D2D2D;
--surface-border: #3F3F3F;
--surface-hover: #2D2D2D;

--text-color: #E0E0E0;
--text-color-secondary: #A0A0A0;
--text-color-emphasis: #FFFFFF;

--shadow-1: 0 1px 3px rgba(0, 0, 0, 0.3);
--shadow-2: 0 4px 6px rgba(0, 0, 0, 0.3);
--shadow-3: 0 10px 15px rgba(0, 0, 0, 0.3);
}

Utilizzo

.my-component {
background-color: var(--surface-card);
color: var(--text-color);
border: 1px solid var(--surface-border);
border-radius: var(--border-radius);
box-shadow: var(--shadow-1);
padding: calc(var(--spacing-unit) * 2);
}

.button-primary {
background-color: var(--color-primary);
color: white;

&:hover {
filter: brightness(1.1);
}
}

Customizzazione Globale

Override PrimeNG

// styles.scss
@import "@primeuix/themes/aura/aura-light-blue/theme.css";

// Override globale
:root {
--primary-color: #FF6B6B;
--primary-color-text: #ffffff;

// Typography
--font-family: 'Inter', sans-serif;
--font-size: 14px;

// Borders
--border-radius: 12px;

// Spacing
--inline-spacing: 0.5rem;
--content-padding: 1.25rem;
}

// Componenti specifici
.p-button {
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}

.p-card {
border: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

Responsive Design

// breakpoints.scss
$breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px
);

@mixin respond-to($breakpoint) {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
}

// Uso
.container {
padding: 1rem;

@include respond-to(md) {
padding: 2rem;
}

@include respond-to(lg) {
padding: 3rem;
}
}

Typography

// typography.scss
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

:root {
--font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;

--font-size-xs: 0.75rem; // 12px
--font-size-sm: 0.875rem; // 14px
--font-size-base: 1rem; // 16px
--font-size-lg: 1.125rem; // 18px
--font-size-xl: 1.25rem; // 20px
--font-size-2xl: 1.5rem; // 24px
--font-size-3xl: 1.875rem; // 30px
--font-size-4xl: 2.25rem; // 36px

--font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;

--line-height-tight: 1.25;
--line-height-normal: 1.5;
--line-height-relaxed: 1.75;
}

body {
font-family: var(--font-family);
font-size: var(--font-size-base);
line-height: var(--line-height-normal);
color: var(--text-color);
}

h1, h2, h3, h4, h5, h6 {
font-weight: var(--font-weight-semibold);
line-height: var(--line-height-tight);
margin-top: 0;
}

h1 { font-size: var(--font-size-4xl); }
h2 { font-size: var(--font-size-3xl); }
h3 { font-size: var(--font-size-2xl); }
h4 { font-size: var(--font-size-xl); }
h5 { font-size: var(--font-size-lg); }
h6 { font-size: var(--font-size-base); }

Utility Classes

// utilities.scss

// Spacing utilities
@for $i from 0 through 8 {
.m-#{$i} { margin: #{$i * 0.5}rem !important; }
.mt-#{$i} { margin-top: #{$i * 0.5}rem !important; }
.mr-#{$i} { margin-right: #{$i * 0.5}rem !important; }
.mb-#{$i} { margin-bottom: #{$i * 0.5}rem !important; }
.ml-#{$i} { margin-left: #{$i * 0.5}rem !important; }

.p-#{$i} { padding: #{$i * 0.5}rem !important; }
.pt-#{$i} { padding-top: #{$i * 0.5}rem !important; }
.pr-#{$i} { padding-right: #{$i * 0.5}rem !important; }
.pb-#{$i} { padding-bottom: #{$i * 0.5}rem !important; }
.pl-#{$i} { padding-left: #{$i * 0.5}rem !important; }
}

// Display utilities
.d-none { display: none !important; }
.d-block { display: block !important; }
.d-flex { display: flex !important; }
.d-grid { display: grid !important; }

// Flex utilities
.flex-row { flex-direction: row !important; }
.flex-column { flex-direction: column !important; }
.justify-content-start { justify-content: flex-start !important; }
.justify-content-center { justify-content: center !important; }
.justify-content-end { justify-content: flex-end !important; }
.justify-content-between { justify-content: space-between !important; }
.align-items-start { align-items: flex-start !important; }
.align-items-center { align-items: center !important; }
.align-items-end { align-items: flex-end !important; }

// Text utilities
.text-left { text-align: left !important; }
.text-center { text-align: center !important; }
.text-right { text-align: right !important; }
.text-uppercase { text-transform: uppercase !important; }
.text-lowercase { text-transform: lowercase !important; }
.text-capitalize { text-transform: capitalize !important; }

Best Practices

  1. CSS Variables: Usa sempre CSS variables per colori, spacing e sizing
  2. Mobile-first: Inizia con design mobile e aggiungi breakpoints per schermi più grandi
  3. BEM Methodology: Usa BEM per naming delle classi CSS custom
  4. SCSS Nesting: Limita nesting a 3 livelli massimo
  5. Component Encapsulation: Usa :host per stili specifici del componente
  6. Global Styles: Mantieni gli stili globali in styles.scss al minimo
  7. Performance: Evita selettori CSS complessi, preferisci classi dirette

Vedi Anche