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
- CSS Variables: Usa sempre CSS variables per colori, spacing e sizing
- Mobile-first: Inizia con design mobile e aggiungi breakpoints per schermi più grandi
- BEM Methodology: Usa BEM per naming delle classi CSS custom
- SCSS Nesting: Limita nesting a 3 livelli massimo
- Component Encapsulation: Usa
:hostper stili specifici del componente - Global Styles: Mantieni gli stili globali in
styles.scssal minimo - Performance: Evita selettori CSS complessi, preferisci classi dirette