Compare commits
	
		
			2 Commits
		
	
	
		
			678ce12de4
			...
			c1ed021196
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | c1ed021196 | |
|  | efb94f14da | 
							
								
								
									
										69
									
								
								README.md
								
								
								
								
							
							
						
						
									
										69
									
								
								README.md
								
								
								
								
							|  | @ -264,6 +264,75 @@ export class AppComponent { | |||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Card Components | ||||
| 
 | ||||
| **Selectors:** `ui-card`, `ui-card-title`, `ui-card-menu`, `ui-card-footer`   | ||||
| **Description:** Zestaw komponentów do tworzenia kart z opcjonalnym nagłówkiem, menu i stopką. | ||||
| 
 | ||||
| **CardComponent (`ui-card`) Properties:** | ||||
| - `title?: string` - Tytuł karty wyświetlany w nagłówku | ||||
| - `margin: 0.5rem` - Domyślny margin karty | ||||
| - Kolory jak ui-menu (--ui-color-menu-bg, --ui-color-menu-text) | ||||
| 
 | ||||
| **Standalone Components:** | ||||
| - `ui-card-title` - Tytuł karty z własnymi stylami | ||||
| - `ui-card-menu` - Menu/przyciski w nagłówku | ||||
| - `ui-card-footer` - Stopka karty z własnymi stylami | ||||
| 
 | ||||
| **Usage:** | ||||
| ```html | ||||
| <!-- Podstawowe użycie --> | ||||
| <ui-card title="Tytuł karty"> | ||||
|   <p>Zawartość karty</p> | ||||
| </ui-card> | ||||
| 
 | ||||
| <!-- Pełne użycie z wszystkimi slotami --> | ||||
| <ui-card title="Statystyki"> | ||||
|   <ui-card-menu> | ||||
|     <button class="btn btn-sm">Edytuj</button> | ||||
|     <button class="btn btn-sm">Usuń</button> | ||||
|   </ui-card-menu> | ||||
|    | ||||
|   <div class="stats"> | ||||
|     <div class="stat-item"> | ||||
|       <span class="value">123</span> | ||||
|       <span class="label">Użytkownicy</span> | ||||
|     </div> | ||||
|   </div> | ||||
|    | ||||
|   <ui-card-footer> | ||||
|     <small class="text-muted">Ostatnia aktualizacja: dziś</small> | ||||
|   </ui-card-footer> | ||||
| </ui-card> | ||||
| 
 | ||||
| <!-- Niestandardowy tytuł --> | ||||
| <ui-card> | ||||
|   <ui-card-title> | ||||
|     <div class="d-flex align-items-center"> | ||||
|       <i class="icon-chart"></i> | ||||
|       <span>Raporty sprzedaży</span> | ||||
|     </div> | ||||
|   </ui-card-title> | ||||
|    | ||||
|   <ui-card-menu> | ||||
|     <span class="text-success">+12%</span> | ||||
|   </ui-card-menu> | ||||
|    | ||||
|   <div class="chart-container"> | ||||
|     <!-- wykres --> | ||||
|   </div> | ||||
| </ui-card> | ||||
| ``` | ||||
| 
 | ||||
| **CSS Variables:** | ||||
| - `--ui-color-card-bg` - tło karty | ||||
| - `--ui-color-card-border` - kolor obramowania | ||||
| - `--ui-color-card-title` - kolor tytułu | ||||
| - `--ui-color-card-text` - kolor tekstu | ||||
| - `--ui-color-card-footer-bg` - tło stopki | ||||
| - `--ui-shadow-card` - standardowy cień karty | ||||
| - `--ui-shadow-card-hover` - cień przy hover | ||||
| 
 | ||||
| ## Development | ||||
| 
 | ||||
| ### Building | ||||
|  |  | |||
|  | @ -4,9 +4,133 @@ | |||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|     padding: 0px 0.75rem; | ||||
|     min-height: 2rem; | ||||
|     border-radius: 4px; | ||||
| 
 | ||||
|     --border-color-rgb: var(--ui-color-background-rgb); | ||||
|     --text-color-rgb: var(--ui-color-text-rgb); | ||||
|     --background-color-rgb: var(--ui-color-background-rgb); | ||||
|     --border-color-alpha: 0; | ||||
|     --background-color-alpha: 0; | ||||
|     --background-hover-rgb: var(--ui-color-content-bg-rgb); | ||||
| 
 | ||||
|     --border-color: rgb(var(--border-color-rgb)); | ||||
|     --text-color: rgb(var(--text-color-rgb)); | ||||
|     --background-color: rgb(var(--background-color-rgb)); | ||||
| 
 | ||||
| 
 | ||||
|     background: rgba(var(--background-color-rgb), var(--background-color-alpha)); | ||||
|     color: var(--text-color); | ||||
|     border: 1px solid rgba(var(--border-color-rgb), var(--border-color-alpha)); | ||||
| 
 | ||||
|     &[size="small"] { | ||||
|         font-size: 0.85rem; | ||||
|         padding: 0px 0.5rem; | ||||
|         min-height: 1.5rem; | ||||
|     } | ||||
| 
 | ||||
|     &[size="large"] { | ||||
|         font-size: 1.2rem; | ||||
|         padding: 0px 1rem; | ||||
|         min-height: 2.5rem; | ||||
|     } | ||||
| 
 | ||||
|     &[theme="primary"] { | ||||
|         --background-color-alpha: 1; | ||||
|         --border-color-alpha: 1; | ||||
|         --background-color-rgb: var(--ui-color-primary-rgb); | ||||
|         --background-hover-rgb: var(--ui-color-primary-hard-rgb); | ||||
|         --text-color-rgb: var(--ui-color-primary-contrast-rgb); | ||||
|         --text-hover-rgb: var(--ui-color-background-rgb); | ||||
|         --border-color-rgb: var(--ui-color-primary-rgb); | ||||
|     } | ||||
| 
 | ||||
|     &[theme="secondary"] { | ||||
|         --background-color-alpha: 1; | ||||
|         --border-color-alpha: 1; | ||||
|         --background-color-rgb: var(--ui-color-secondary-rgb); | ||||
|         --background-hover-rgb: var(--ui-color-secondary-hard-rgb); | ||||
|         --text-color-rgb: var(--ui-color-secondary-contrast-rgb); | ||||
|         --text-hover-rgb: var(--ui-color-background-rgb); | ||||
|         --border-color-rgb: var(--ui-color-secondary-rgb); | ||||
|     } | ||||
| 
 | ||||
|     &[theme="danger"] { | ||||
|         --background-color-alpha: 1; | ||||
|         --border-color-alpha: 1; | ||||
|         --background-color-rgb: var(--ui-color-danger-rgb); | ||||
|         --background-hover-rgb: var(--ui-color-danger-hard-rgb); | ||||
|         --text-color-rgb: var(--ui-color-danger-contrast-rgb); | ||||
|         --text-hover-rgb: var(--ui-color-background-rgb); | ||||
|         --border-color-rgb: var(--ui-color-danger-rgb); | ||||
|     } | ||||
| 
 | ||||
|     &[theme="warning"] { | ||||
|         --background-color-alpha: 1; | ||||
|         --border-color-alpha: 1; | ||||
|         --background-color-rgb: var(--ui-color-warning-rgb); | ||||
|         --background-hover-rgb: var(--ui-color-warning-hard-rgb); | ||||
|         --text-color-rgb: var(--ui-color-warning-contrast-rgb); | ||||
|         --text-hover-rgb: var(--ui-color-background-rgb); | ||||
|         --border-color-rgb: var(--ui-color-warning-rgb); | ||||
|     } | ||||
| 
 | ||||
|     &[theme="info"] { | ||||
|         --background-color-alpha: 1; | ||||
|         --border-color-alpha: 1; | ||||
|         --background-color-rgb: var(--ui-color-info-rgb); | ||||
|         --background-hover-rgb: var(--ui-color-info-hard-rgb); | ||||
|         --text-color-rgb: var(--ui-color-info-contrast-rgb); | ||||
|         --text-hover-rgb: var(--ui-color-background-rgb); | ||||
|         --border-color-rgb: var(--ui-color-info-rgb); | ||||
|     } | ||||
| 
 | ||||
|     &[theme="success"] { | ||||
|         --background-color-alpha: 1; | ||||
|         --border-color-alpha: 1; | ||||
|         --background-color-rgb: var(--ui-color-success-rgb); | ||||
|         --background-hover-rgb: var(--ui-color-success-hard-rgb); | ||||
|         --text-hover-rgb: var(--ui-color-background-rgb); | ||||
|         --border-color-rgb: var(--ui-color-success-rgb); | ||||
|         &:not(&[outlined=true]) { | ||||
|             --text-color-rgb: var(--ui-color-success-contrast-rgb); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &[outlined="true"] { | ||||
|         --background-color-alpha: 0; | ||||
|         --border-color-alpha: 1; | ||||
|         --text-color-rgb: var(--border-color-rgb); | ||||
|         &[theme="default"] { | ||||
|             --text-color-rgb: var(--ui-color-text-rgb); | ||||
|             --border-color-rgb: var(--ui-color-text-rgb); | ||||
|             --background-hover-rgb: var(--ui-color-text-rgb); | ||||
|             --text-hover-rgb: var(--ui-color-background-rgb); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     &.active, | ||||
|     &.focus, | ||||
|     &:active, | ||||
|     &:hover{ | ||||
|         background: var(--ui-color-button-hover, rgba(255, 255, 255, 0.1)); | ||||
|         --background-color-alpha: 1; | ||||
|         --border-color-alpha: 1; | ||||
|         --background-color-rgb: var(--background-hover-rgb); | ||||
|         --border-color-rgb: var(--background-hover-rgb); | ||||
|         --text-color-rgb: var(--text-hover-rgb); | ||||
| 
 | ||||
|         &[outlined=true] { | ||||
|             --text-color-rgb: var(--text-hover-rgb); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     & ::ng-deep ~ ui-button{ | ||||
|         margin-left: 0.25rem; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| ::ng-deep ui-header > ui-button{ | ||||
|     border-radius: 0px !important; | ||||
| } | ||||
|  |  | |||
|  | @ -1,4 +1,6 @@ | |||
| import { Component } from '@angular/core'; | ||||
| import { Component, HostBinding, Input } from '@angular/core'; | ||||
| 
 | ||||
| export type ButtonTheme = 'default' | 'primary' | 'secondary' | 'danger' | 'warning' | 'info' | 'success'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'ui-button', | ||||
|  | @ -7,5 +9,16 @@ import { Component } from '@angular/core'; | |||
|   styleUrl: './button.component.scss' | ||||
| }) | ||||
| export class ButtonComponent { | ||||
|     @Input() | ||||
|     @HostBinding('attr.theme') | ||||
|     theme: ButtonTheme = 'default'; | ||||
| 
 | ||||
|     @Input() | ||||
|     @HostBinding('attr.outlined') | ||||
|     outlined: boolean | string = false; | ||||
| 
 | ||||
|     @Input() | ||||
|     @HostBinding('attr.size') | ||||
|     size: 'small' | 'large' | 'default' = 'default'; | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,186 @@ | |||
| # Card Components | ||||
| 
 | ||||
| Zestaw komponentów do tworzenia kart z opcjonalnym nagłówkiem, menu i stopką. | ||||
| 
 | ||||
| ## Komponenty | ||||
| 
 | ||||
| - `ui-card` - główny kontener karty | ||||
| - `ui-card-title` - tytuł karty (standalone komponent) | ||||
| - `ui-card-menu` - menu/przyciski w nagłówku (standalone komponent)   | ||||
| - `ui-card-footer` - stopka karty (standalone komponent) | ||||
| 
 | ||||
| ## Użycie | ||||
| 
 | ||||
| ### Podstawowe użycie z tytułem jako Input | ||||
| 
 | ||||
| ```html | ||||
| <ui-card title="Tytuł karty"> | ||||
|   <p>Zawartość karty</p> | ||||
| </ui-card> | ||||
| ``` | ||||
| 
 | ||||
| ### Użycie z komponentem ui-card-title | ||||
| 
 | ||||
| ```html | ||||
| <ui-card> | ||||
|   <ui-card-title> | ||||
|     <h2>Niestandardowy tytuł</h2> | ||||
|     <span class="badge">Nowy</span> | ||||
|   </ui-card-title> | ||||
|   <p>Zawartość karty</p> | ||||
| </ui-card> | ||||
| ``` | ||||
| 
 | ||||
| ### Pełne użycie z wszystkimi komponentami | ||||
| 
 | ||||
| ```html | ||||
| <ui-card title="Statystyki"> | ||||
|   <ui-card-menu> | ||||
|     <button class="btn btn-sm">Edytuj</button> | ||||
|     <button class="btn btn-sm">Usuń</button> | ||||
|   </ui-card-menu> | ||||
|    | ||||
|   <div class="stats"> | ||||
|     <div class="stat-item"> | ||||
|       <span class="value">123</span> | ||||
|       <span class="label">Użytkownicy</span> | ||||
|     </div> | ||||
|     <div class="stat-item"> | ||||
|       <span class="value">456</span> | ||||
|       <span class="label">Zamówienia</span> | ||||
|     </div> | ||||
|   </div> | ||||
|    | ||||
|   <ui-card-footer> | ||||
|     <small class="text-muted">Ostatnia aktualizacja: dziś</small> | ||||
|   </ui-card-footer> | ||||
| </ui-card> | ||||
| ``` | ||||
| 
 | ||||
| ### Użycie z komponentami ui-card-title i ui-card-menu | ||||
| 
 | ||||
| ```html | ||||
| <ui-card> | ||||
|   <ui-card-title> | ||||
|     <div class="d-flex align-items-center"> | ||||
|       <i class="icon-chart"></i> | ||||
|       <span>Raporty sprzedaży</span> | ||||
|     </div> | ||||
|   </ui-card-title> | ||||
|    | ||||
|   <ui-card-menu> | ||||
|     <div class="stats-mini"> | ||||
|       <span class="text-success">+12%</span> | ||||
|     </div> | ||||
|     <button class="btn btn-outline-primary btn-sm">Szczegóły</button> | ||||
|   </ui-card-menu> | ||||
|    | ||||
|   <div class="chart-container"> | ||||
|     <!-- wykres --> | ||||
|   </div> | ||||
|    | ||||
|   <ui-card-footer> | ||||
|     <div class="d-flex justify-content-between"> | ||||
|       <span>Okres: ostatnie 30 dni</span> | ||||
|       <a href="#" class="text-primary">Zobacz więcej</a> | ||||
|     </div> | ||||
|   </ui-card-footer> | ||||
| </ui-card> | ||||
| ``` | ||||
| 
 | ||||
| ## API | ||||
| 
 | ||||
| ### CardComponent (`ui-card`) | ||||
| 
 | ||||
| **Inputs:** | ||||
| | Właściwość | Typ | Domyślna | Opis | | ||||
| |------------|-----|----------|------| | ||||
| | `title` | `string` | `undefined` | Tytuł karty wyświetlany w nagłówku | | ||||
| 
 | ||||
| **Content Projection:** | ||||
| | Selektor | Opis | | ||||
| |----------|------| | ||||
| | `ui-card-title` | Komponent tytułu karty | | ||||
| | `ui-card-menu` | Komponent menu w nagłówku | | ||||
| | (default) | Główna zawartość karty | | ||||
| | `ui-card-footer` | Komponent stopki karty | | ||||
| 
 | ||||
| ### CardTitleComponent (`ui-card-title`) | ||||
| 
 | ||||
| **Opis:** Standalone komponent do wyświetlania tytułu karty z własnymi stylami. | ||||
| 
 | ||||
| ### CardMenuComponent (`ui-card-menu`) | ||||
| 
 | ||||
| **Opis:** Standalone komponent do wyświetlania menu/przycisków w nagłówku karty. | ||||
| 
 | ||||
| ### CardFooterComponent (`ui-card-footer`) | ||||
| 
 | ||||
| **Opis:** Standalone komponent do wyświetlania stopki karty z własnymi stylami. | ||||
| 
 | ||||
| ## Zmienne CSS | ||||
| 
 | ||||
| Komponent używa następujących zmiennych CSS z systemu motywów: | ||||
| 
 | ||||
| ### Kolory | ||||
| - `--ui-color-card-bg` - tło karty | ||||
| - `--ui-color-card-border` - kolor obramowania | ||||
| - `--ui-color-card-title` - kolor tytułu | ||||
| - `--ui-color-card-text` - kolor tekstu | ||||
| - `--ui-color-card-footer-bg` - tło stopki | ||||
| 
 | ||||
| ### Spacing | ||||
| - `--ui-spacing-xs` (4px) - małe odstępy | ||||
| - `--ui-spacing-sm` (8px) - średnie odstępy   | ||||
| - `--ui-spacing-md` (16px) - standardowe padding | ||||
| - `--ui-spacing-lg` (24px) - duże odstępy | ||||
| 
 | ||||
| ### Layout | ||||
| - `--ui-border-radius` (8px) - zaokrąglenie rogów | ||||
| - `--ui-font-size-lg` (18px) - rozmiar czcionki tytułu | ||||
| - `--ui-font-weight-semibold` (600) - grubość czcionki tytułu | ||||
| - `--ui-card-header-min-height` (40px) - minimalna wysokość nagłówka | ||||
| 
 | ||||
| ### Cienie | ||||
| - `--ui-shadow-card` - standardowy cień karty | ||||
| - `--ui-shadow-card-hover` - cień przy hover | ||||
| 
 | ||||
| ## Responsywność | ||||
| 
 | ||||
| Na urządzeniach mobilnych (max-width: 768px): | ||||
| - Nagłówek karty zmienia się na układ kolumnowy | ||||
| - Menu wyrównuje się do prawej strony | ||||
| - Zachowane są wszystkie funkcjonalności | ||||
| 
 | ||||
| ## Przykłady stylizacji | ||||
| 
 | ||||
| ### Karta z kolorowym nagłówkiem | ||||
| 
 | ||||
| ```scss | ||||
| .custom-card { | ||||
|   .ui-card-header { | ||||
|     background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | ||||
|     color: white; | ||||
|     margin: -16px -16px 0; | ||||
|     padding: 16px; | ||||
|   } | ||||
|    | ||||
|   .ui-card-title { | ||||
|     color: white; | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Karta z ikoną | ||||
| 
 | ||||
| ```html | ||||
| <ui-card> | ||||
|   <ui-card-title> | ||||
|     <div class="d-flex align-items-center gap-2"> | ||||
|       <i class="icon-settings"></i> | ||||
|       <span>Ustawienia</span> | ||||
|     </div> | ||||
|   </ui-card-title> | ||||
|    | ||||
|   <!-- zawartość --> | ||||
| </ui-card> | ||||
| ``` | ||||
|  | @ -0,0 +1,7 @@ | |||
| :host { | ||||
|     padding: 0.5rem; | ||||
|     display: flex; | ||||
|     &:empty{ | ||||
|         display: none; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| import { Component } from '@angular/core'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'ui-card-footer', | ||||
|   standalone: true, | ||||
|   imports: [], | ||||
|   template: '<ng-content />', | ||||
|   styleUrl: './card-footer.component.scss' | ||||
| }) | ||||
| export class CardFooterComponent { | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,7 @@ | |||
| :host { | ||||
|   display: flex; | ||||
|   gap: var(--ui-spacing-sm, 8px); | ||||
|   align-items: flex-start; | ||||
|   flex-shrink: 0; | ||||
|   padding: 1rem; | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| import { Component } from '@angular/core'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'ui-card-menu', | ||||
|   standalone: true, | ||||
|   imports: [], | ||||
|   template: '<ng-content />', | ||||
|   styleUrl: './card-menu.component.scss' | ||||
| }) | ||||
| export class CardMenuComponent { | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| :host { | ||||
|   display: block; | ||||
|   margin: 0; | ||||
|   font-size: var(--ui-font-size-lg, 18px); | ||||
|   font-weight: var(--ui-font-weight-semibold, 600); | ||||
|   color: var(--ui-color-menu-text, #ffffff); | ||||
|   line-height: 1.2; | ||||
|   padding: 1rem; | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| import { Component } from '@angular/core'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'ui-card-title', | ||||
|   standalone: true, | ||||
|   imports: [], | ||||
|   template: `<ng-content />`, | ||||
|   styleUrl: './card-title.component.scss' | ||||
| }) | ||||
| export class CardTitleComponent { | ||||
| 
 | ||||
| } | ||||
|  | @ -1 +1,15 @@ | |||
| <p>card works!</p> | ||||
| <div class="header"> | ||||
|     @if (title) { | ||||
|         <div class="title">{{ title }}</div> | ||||
|     } @else { | ||||
|         <ng-content select="ui-card-title"></ng-content> | ||||
|     } | ||||
|     <ng-content select="ui-card-menu"></ng-content> | ||||
| 
 | ||||
| </div> | ||||
| 
 | ||||
| <div class="content"> | ||||
|   <ng-content></ng-content> | ||||
| </div> | ||||
| 
 | ||||
| <ng-content select="ui-card-footer"></ng-content> | ||||
|  |  | |||
|  | @ -0,0 +1,23 @@ | |||
| :host { | ||||
|   display: block; | ||||
|   margin: var(--ui-spacing-sm, 0.5rem); | ||||
|   background: var(--ui-color-menu-bg, #343a40); | ||||
|   border: 1px solid var(--ui-color-menu-border, #d0d0d0); | ||||
|   border-radius: var(--ui-border-radius, 8px); | ||||
|   box-shadow: var(--ui-shadow-card, 0 2px 4px rgba(0, 0, 0, 0.1)); | ||||
|   overflow: hidden; | ||||
|   transition: box-shadow 0.2s ease; | ||||
| } | ||||
| 
 | ||||
| .content { | ||||
|   padding: var(--ui-spacing-md, 16px); | ||||
|   color: var(--ui-color-menu-text, #ffffff); | ||||
|   line-height: 1.5; | ||||
| } | ||||
| .header{ | ||||
|     display: flex; | ||||
|     .title, | ||||
|     ::ng-deep ui-card-title{ | ||||
|         flex-grow: 1; | ||||
|     } | ||||
| } | ||||
|  | @ -1,11 +1,13 @@ | |||
| import { Component } from '@angular/core'; | ||||
| import { Component, Input } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'ui-card', | ||||
|   imports: [], | ||||
|   standalone: true, | ||||
|   imports: [CommonModule], | ||||
|   templateUrl: './card.component.html', | ||||
|   styleUrl: './card.component.scss' | ||||
| }) | ||||
| export class CardComponent { | ||||
| 
 | ||||
|   @Input() title?: string; | ||||
| } | ||||
|  |  | |||
|  | @ -1 +1,18 @@ | |||
| <p>input works!</p> | ||||
| <div  (click)="stopPropagation($event)"> | ||||
|     <ng-content select="[place=before]"></ng-content> | ||||
| </div> | ||||
| <input | ||||
|     #input | ||||
|     [type]="type" | ||||
|     [value]="value" | ||||
|     [disabled]="disabled" | ||||
|     [required]="required" | ||||
|     (input)="onInputChange($event)" | ||||
|     (focus)="onInputFocus()" | ||||
|     (blur)="onInputBlur()" | ||||
|     class="ui-input-field" | ||||
| /> | ||||
| <label class="ui-input-label" [class.floating]="isFocused || filled">{{ placeholder }}</label> | ||||
| <div (click)="stopPropagation($event)"> | ||||
|     <ng-content select="[place=after]"></ng-content> | ||||
| </div> | ||||
|  |  | |||
|  | @ -0,0 +1,94 @@ | |||
| :host { | ||||
|     display: block; | ||||
|     width: 100%; | ||||
| 
 | ||||
|     --background-color-rgb: var(--ui-color-input-bg-rgb, 255, 255, 255); | ||||
|     --accent-color-rgb: var(--ui-color-input-label-focus-rgb, 0, 123, 255); | ||||
|     --text-color-rgb: var(--ui-color-input-text-rgb, 33, 37, 41); | ||||
| 
 | ||||
|     --background-color: var(--ui-color-input-bg, rgb(var(--background-color-rgb))); | ||||
|     --accent-color: var(--ui-color-input-label-focus, rgb(var(--accent-color-rgb))); | ||||
|     --text-color: var(--ui-color-input-text, rgb(var(--text-color-rgb))); | ||||
| 
 | ||||
|     background-color: var(--background-color); | ||||
|     box-shadow: 0px 0px 0px 0px var(--background-color), 0px 0px 0px 0px var(--accent-color); | ||||
|     transition: all 0.2s ease-in-out; | ||||
| 
 | ||||
|     position: relative; | ||||
|     display: flex; | ||||
|     align-items: stretch; | ||||
|     gap: 0.5rem; | ||||
|     cursor: text; | ||||
|     min-height: 2rem; | ||||
|     &.invalid{ | ||||
|         --accent-color: var(--ui-color-input-label-invalid, #dc3545); | ||||
|     } | ||||
| 
 | ||||
|     &.active:not(.disabled), | ||||
|     &.focused{ | ||||
|         box-shadow: 0px 0px 0px 9px var(--background-color), 0px 0px 0px 10px var(--accent-color); | ||||
|     } | ||||
|     &.hovered { | ||||
|         box-shadow: 0px 0px 0px 0px var(--background-color), 0px 0px 0px 1px var(--accent-color); | ||||
|     } | ||||
|     &.active:not(.disabled), | ||||
|     &.focused, | ||||
|     &.hovered { | ||||
|         --background-color: var(--ui-color-input-bg-hover, #313b44); | ||||
|     } | ||||
|     &.disabled { | ||||
|         opacity: 0.6; | ||||
|         cursor: not-allowed; | ||||
|     } | ||||
|     .ui-input-label { | ||||
|         position: absolute; | ||||
|         left: 0.5rem; | ||||
|         top: 50%; | ||||
|         transform: translateY(-50%); | ||||
|         font-size: 1rem; | ||||
|         color: rgba(var(--accent-color-rgb), 0.4); | ||||
|         pointer-events: none; | ||||
|         transition: all 0.2s ease-in-out; | ||||
|         background-color: transparent; | ||||
|         border-radius: 6px; | ||||
|         padding: 0 0.25rem; | ||||
|         white-space: nowrap; | ||||
| 
 | ||||
|         &.floating { | ||||
|             top: 0; | ||||
|             color: var(--accent-color); | ||||
|             transform: translateY(-50%); | ||||
|             font-size: 0.7rem; | ||||
|             background-color: var(--background-color); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ::ng-deep { | ||||
|         > div { | ||||
|             display: flex; | ||||
|             > [place] { | ||||
|                 display: flex; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| .ui-input-field { | ||||
|     flex: 1; | ||||
|     padding: 0.5rem 0.25rem; | ||||
|     border: none; | ||||
|     outline: none; | ||||
|     background: transparent; | ||||
|     font-size: 1rem; | ||||
|     line-height: 1.5; | ||||
|     color: var(--ui-color-input-text, #212529); | ||||
| 
 | ||||
|     &::placeholder { | ||||
|         color: transparent; | ||||
|     } | ||||
| 
 | ||||
|     &:disabled { | ||||
|         cursor: not-allowed; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -1,23 +0,0 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||||
| 
 | ||||
| import { InputComponent } from './input.component'; | ||||
| 
 | ||||
| describe('InputComponent', () => { | ||||
|   let component: InputComponent; | ||||
|   let fixture: ComponentFixture<InputComponent>; | ||||
| 
 | ||||
|   beforeEach(async () => { | ||||
|     await TestBed.configureTestingModule({ | ||||
|       imports: [InputComponent] | ||||
|     }) | ||||
|     .compileComponents(); | ||||
| 
 | ||||
|     fixture = TestBed.createComponent(InputComponent); | ||||
|     component = fixture.componentInstance; | ||||
|     fixture.detectChanges(); | ||||
|   }); | ||||
| 
 | ||||
|   it('should create', () => { | ||||
|     expect(component).toBeTruthy(); | ||||
|   }); | ||||
| }); | ||||
|  | @ -1,11 +1,134 @@ | |||
| import { Component } from '@angular/core'; | ||||
| import { Component, Input, HostBinding, ElementRef, forwardRef, HostListener } from '@angular/core'; | ||||
| import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| import { FormModule } from '../../form-module'; | ||||
| 
 | ||||
| @Component({ | ||||
|   selector: 'ui-input', | ||||
|   imports: [], | ||||
|   exportAs: 'uiInput', | ||||
|   standalone: true, | ||||
|   imports: [CommonModule], | ||||
|   templateUrl: './input.component.html', | ||||
|   styleUrl: './input.component.scss' | ||||
| }) | ||||
| export class InputComponent { | ||||
| 
 | ||||
|   styleUrl: './input.component.scss', | ||||
|   providers: [ | ||||
|     { | ||||
|       provide: NG_VALUE_ACCESSOR, | ||||
|       useExisting: forwardRef(() => InputComponent), | ||||
|       multi: true | ||||
|     } | ||||
|   ] | ||||
| }) | ||||
| export class InputComponent implements ControlValueAccessor { | ||||
|   @Input() | ||||
|   placeholder: string = ''; | ||||
| 
 | ||||
|   @Input() | ||||
|   type: string = 'text'; | ||||
| 
 | ||||
|   @Input() | ||||
|   @HostBinding('class.disabled') | ||||
|   disabled: boolean = false; | ||||
| 
 | ||||
|   @Input() | ||||
|   required: boolean = false; | ||||
| 
 | ||||
|   value: string = ''; | ||||
| 
 | ||||
|   @HostBinding('class.focused') | ||||
|   isFocused: boolean = false; | ||||
| 
 | ||||
|   @HostBinding('class.hovered') | ||||
|   isHovered: boolean = false; | ||||
| 
 | ||||
|   isTouched: boolean = false; | ||||
| 
 | ||||
|   @HostBinding('class.active') | ||||
|   isActive: boolean = false; | ||||
| 
 | ||||
|   private onChange = (value: string) => {}; | ||||
|   private onTouched = () => {}; | ||||
| 
 | ||||
|   @HostBinding('class.filled') | ||||
|   get filled() { | ||||
|     return this.value && this.value.length > 0; | ||||
|   } | ||||
| 
 | ||||
|   @HostBinding('class.invalid') | ||||
|   get invalid() { | ||||
|     return this.required && this.isTouched && (!this.value || this.value.length === 0); | ||||
|   } | ||||
| 
 | ||||
|   constructor(private elementRef: ElementRef) {} | ||||
| 
 | ||||
|   public stopPropagation(event: Event) { | ||||
|     event.stopPropagation(); | ||||
|   } | ||||
| 
 | ||||
|   public focus() { | ||||
|     console.log({elementRef: this.elementRef.nativeElement}); | ||||
|     this.elementRef.nativeElement.querySelector('input')?.focus(); | ||||
|     this.onInputFocus(); | ||||
|   } | ||||
| 
 | ||||
|   public blur() { | ||||
|     this.elementRef.nativeElement.querySelector('input')?.blur(); | ||||
|     this.onInputBlur(); | ||||
|   } | ||||
| 
 | ||||
|   @HostListener('mouseenter') | ||||
|   public hover() { | ||||
|     this.isHovered = true; | ||||
|   } | ||||
| 
 | ||||
|   @HostListener('mouseleave') | ||||
|   public leave() { | ||||
|     this.isHovered = false; | ||||
|   } | ||||
| 
 | ||||
|   onInputChange(event: Event): void { | ||||
|     const target = event.target as HTMLInputElement; | ||||
|     this.value = target.value; | ||||
|     this.onChange(this.value); | ||||
|   } | ||||
| 
 | ||||
|   onInputFocus(): void { | ||||
|     this.isFocused = true; | ||||
|     this.isActive = true; | ||||
|     FormModule.ActiveInput = this; | ||||
|     (window as any).KeyEventSafeModeEnabled = true; | ||||
|   } | ||||
| 
 | ||||
|   onInputBlur(): void { | ||||
|     this.isFocused = false; | ||||
|     this.isActive = false; | ||||
|     this.isTouched = true; | ||||
|     this.onTouched(); | ||||
|     FormModule.ActiveInput = undefined; | ||||
|     (window as any).KeyEventSafeModeEnabled = undefined; | ||||
|   } | ||||
| 
 | ||||
|   @HostListener('click') | ||||
|   onContainerClick(): void { | ||||
|     const input = this.elementRef.nativeElement.querySelector('input'); | ||||
|     if (input && !this.disabled) { | ||||
|       this.focus(); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // ControlValueAccessor implementation
 | ||||
|   writeValue(value: string): void { | ||||
|     this.value = value || ''; | ||||
|   } | ||||
| 
 | ||||
|   registerOnChange(fn: (value: string) => void): void { | ||||
|     this.onChange = fn; | ||||
|   } | ||||
| 
 | ||||
|   registerOnTouched(fn: () => void): void { | ||||
|     this.onTouched = fn; | ||||
|   } | ||||
| 
 | ||||
|   setDisabledState(isDisabled: boolean): void { | ||||
|     this.disabled = isDisabled; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -1,12 +1,17 @@ | |||
| import { NgModule } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| 
 | ||||
| 
 | ||||
| import { InputComponent } from './components/input/input.component'; | ||||
| 
 | ||||
| @NgModule({ | ||||
|   declarations: [], | ||||
|   imports: [ | ||||
|     CommonModule | ||||
|     CommonModule, | ||||
|     InputComponent | ||||
|   ], | ||||
|   exports: [ | ||||
|     InputComponent | ||||
|   ] | ||||
| }) | ||||
| export class FormModule { } | ||||
| export class FormModule { | ||||
|     public static ActiveInput?: InputComponent; | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,141 @@ | |||
| # UI Contrast Color Function | ||||
| 
 | ||||
| ## Opis | ||||
| 
 | ||||
| Funkcja `ui-contrast-color()` automatycznie wybiera najlepszy kolor kontrastujący na podstawie jasności koloru bazowego. Jest to narzędzie do zapewnienia optymalnej czytelności tekstu na różnych tłach. | ||||
| 
 | ||||
| ## Składnia | ||||
| 
 | ||||
| ```scss | ||||
| @function ui-contrast-color($base-color, $light-color, $dark-color) | ||||
| ``` | ||||
| 
 | ||||
| ### Parametry | ||||
| 
 | ||||
| - `$base-color` - Kolor bazowy, dla którego sprawdzana jest jasność | ||||
| - `$light-color` - Kolor zwracany dla ciemnych kolorów bazowych (zwykle kolor tekstu w jasnym motywie lub tła w ciemnym motywie) | ||||
| - `$dark-color` - Kolor zwracany dla jasnych kolorów bazowych (zwykle kolor tła w jasnym motywie lub tekstu w ciemnym motywie) | ||||
| 
 | ||||
| ### Zwracana wartość | ||||
| 
 | ||||
| Funkcja zwraca: | ||||
| - `$dark-color` jeśli jasność `$base-color` > 50% (kolor bliżej białego) | ||||
| - `$light-color` jeśli jasność `$base-color` ≤ 50% (kolor bliżej czarnego) | ||||
| 
 | ||||
| ## Mechanizm działania | ||||
| 
 | ||||
| 1. Funkcja oblicza jasność koloru bazowego używając `color.channel($base-color, "lightness", $space: hsl)` | ||||
| 2. Porównuje jasność z progiem 50% | ||||
| 3. Zwraca odpowiedni kolor kontrastujący | ||||
| 
 | ||||
| ## Przykłady użycia | ||||
| 
 | ||||
| ### Podstawowe użycie | ||||
| 
 | ||||
| ```scss | ||||
| @use 'theme-mixins' as theme; | ||||
| 
 | ||||
| .button { | ||||
|   $bg: #007bff; // Niebieski | ||||
|   background-color: $bg; | ||||
|   color: theme.ui-contrast-color($bg, #ffffff, #333333); | ||||
|   // Wynik: biały tekst, bo niebieski jest ciemny | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Kolory statusów | ||||
| 
 | ||||
| ```scss | ||||
| .status-badge { | ||||
|   &.success { | ||||
|     $color: #28a745; // Zielony | ||||
|     background-color: $color; | ||||
|     color: theme.ui-contrast-color($color, white, black); | ||||
|     // Wynik: biały tekst | ||||
|   } | ||||
|    | ||||
|   &.warning { | ||||
|     $color: #ffc107; // Żółty | ||||
|     background-color: $color; | ||||
|     color: theme.ui-contrast-color($color, white, black); | ||||
|     // Wynik: czarny tekst (żółty jest jasny) | ||||
|   } | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Użycie z motywami | ||||
| 
 | ||||
| ```scss | ||||
| // Dla jasnego motywu | ||||
| .component.light-theme { | ||||
|   $bg: var(--ui-color-primary); | ||||
|   $primary: #2c3e50; // Wartość z motywu | ||||
|    | ||||
|   background-color: $primary; | ||||
|   color: theme.ui-contrast-color($primary, #ffffff, #333333); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## Integracja z systemem motywów | ||||
| 
 | ||||
| Funkcja jest już zintegrowana z systemem motywów biblioteki UI i automatycznie generuje zmienne contrast: | ||||
| 
 | ||||
| ### Zmienne HEX | ||||
| - `--ui-color-primary-contrast` | ||||
| - `--ui-color-secondary-contrast` | ||||
| - `--ui-color-success-contrast` | ||||
| - `--ui-color-warning-contrast` | ||||
| - `--ui-color-danger-contrast` | ||||
| - `--ui-color-info-contrast` | ||||
| 
 | ||||
| ### Zmienne RGB | ||||
| - `--ui-color-primary-contrast-rgb` | ||||
| - `--ui-color-secondary-contrast-rgb` | ||||
| - `--ui-color-success-contrast-rgb` | ||||
| - `--ui-color-warning-contrast-rgb` | ||||
| - `--ui-color-danger-contrast-rgb` | ||||
| - `--ui-color-info-contrast-rgb` | ||||
| 
 | ||||
| ## Użycie zmiennych CSS | ||||
| 
 | ||||
| ```scss | ||||
| .dynamic-component { | ||||
|   background-color: var(--ui-color-primary); | ||||
|   color: var(--ui-color-primary-contrast); | ||||
|    | ||||
|   // Z przezroczystością | ||||
|   background-color: rgba(var(--ui-color-primary-rgb), 0.8); | ||||
|   color: var(--ui-color-primary-contrast); | ||||
|    | ||||
|   // Obramowanie z przezroczystym kontrastem | ||||
|   border: 1px solid rgba(var(--ui-color-primary-contrast-rgb), 0.3); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ## Zalety | ||||
| 
 | ||||
| 1. **Automatyczny dobór** - Nie musisz ręcznie sprawdzać, czy kolor jest jasny czy ciemny | ||||
| 2. **Spójność** - Wszystkie komponenty używają tej samej logiki kontrastowania | ||||
| 3. **Dostępność** - Zapewnia lepszą czytelność dla użytkowników | ||||
| 4. **Elastyczność** - Możesz definiować własne kolory jasne i ciemne dla różnych kontekstów | ||||
| 5. **Integracja z motywami** - Automatycznie generuje zmienne CSS dla wszystkich motywów | ||||
| 
 | ||||
| ## Uwagi techniczne | ||||
| 
 | ||||
| - Funkcja działa w czasie kompilacji SCSS z konkretnymi wartościami kolorów | ||||
| - Dla dynamicznych kolorów CSS używaj pre-wygenerowanych zmiennych `*-contrast` | ||||
| - Próg 50% jasności jest optymalny dla większości przypadków użycia | ||||
| - Używa nowoczesnej składni Sass (`color.channel()`) bez ostrzeżeń o deprecacji | ||||
| 
 | ||||
| ## Przykłady kolorów i wyników | ||||
| 
 | ||||
| | Kolor bazowy | Nazwa | Jasność | Wynik dla (white, black) | | ||||
| |--------------|-------|---------|--------------------------| | ||||
| | `#ffffff` | Biały | 100% | `black` | | ||||
| | `#ffc107` | Żółty | ~75% | `black` | | ||||
| | `#28a745` | Zielony | ~45% | `white` | | ||||
| | `#007bff` | Niebieski | ~35% | `white` | | ||||
| | `#dc3545` | Czerwony | ~30% | `white` | | ||||
| | `#000000` | Czarny | 0% | `white` | | ||||
| 
 | ||||
| Funkcja zapewnia optymalny kontrast dla wszystkich kolorów w systemie motywów biblioteki UI. | ||||
|  | @ -1,5 +1,22 @@ | |||
| /* Theme Integration Mixins for UI Library */ | ||||
| 
 | ||||
| @use 'sass:color'; | ||||
| 
 | ||||
| /** | ||||
|  * Mixin that returns the best contrasting color based on lightness | ||||
|  * If the base color is closer to white than black, returns the dark color | ||||
|  * If the base color is closer to black than white, returns the light color | ||||
|  * | ||||
|  * @param $base-color - The color to check lightness for | ||||
|  * @param $light-color - Color to return for dark base colors (usually text color in light theme or background in dark theme) | ||||
|  * @param $dark-color - Color to return for light base colors (usually background color in light theme or text color in dark theme) | ||||
|  * @return Color - The contrasting color | ||||
|  */ | ||||
| @function ui-contrast-color($base-color, $light-color, $dark-color) { | ||||
|   $lightness: color.channel($base-color, "lightness", $space: hsl); | ||||
|   @return if($lightness > 40%, $dark-color, $light-color); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Mixin that generates CSS variables for UI library components | ||||
|  * This mixin should be called by the main application to provide colors for the UI library | ||||
|  | @ -18,29 +35,181 @@ | |||
|   $background: #343a40, | ||||
|   $text: #ffffff, | ||||
|   $border: #d0d0d0, | ||||
|   $hover: rgba(255, 255, 255, 0.1) | ||||
|   $hover: rgba(255, 255, 255, 0.1), | ||||
|   $success: #28a745, | ||||
|   $warning: #ffc107, | ||||
|   $danger: #dc3545, | ||||
|   $info: #17a2b8 | ||||
| ) { | ||||
|   /* UI Library specific CSS variables - only these should be used in UI components */ | ||||
|   --ui-color-primary: #{$primary}; | ||||
|   --ui-color-secondary: #{$secondary}; | ||||
|   --ui-color-background: #{$background}; | ||||
|   --ui-color-text: #{$text}; | ||||
|   --ui-color-border: #{$border}; | ||||
|   --ui-color-hover: #{$hover}; | ||||
|   /* Assign SCSS variables for each color */ | ||||
|   $primary-color: $primary; | ||||
|   $secondary-color: $secondary; | ||||
|   $background-color: $background; | ||||
|   $text-color: $text; | ||||
|   $border-color: $border; | ||||
|   $hover-color: $hover; | ||||
|   $success-color: $success; | ||||
|   $warning-color: $warning; | ||||
|   $danger-color: $danger; | ||||
|   $info-color: $info; | ||||
| 
 | ||||
|   /* Derived colors for specific UI components */ | ||||
|   --ui-color-menu-bg: #{$background}; | ||||
|   --ui-color-menu-text: #{$text}; | ||||
|   --ui-color-menu-hover: #{$hover}; | ||||
|   --ui-color-menu-active: color-mix(in srgb, #{$background} 80%, black 20%); | ||||
|   --ui-color-menu-border: #{$border}; | ||||
|   /* Derived colors as SCSS variables using SCSS functions */ | ||||
|   $menu-active-color: color.mix(black, $background-color, 20%); | ||||
|   $content-bg-color: color.mix(white, $background-color, 5%); | ||||
|   $icon-muted-color: rgba($text-color, 0.6); | ||||
|   $card-bg-color: if($background-color == #343a40, #ffffff, color.mix(white, $background-color, 5%)); | ||||
|   $card-text-color: if($text-color == #ffffff, #333333, $text-color); | ||||
|   $card-footer-bg-color: if($background-color == #343a40, #f8f9fa, color.mix(white, $background-color, 10%)); | ||||
| 
 | ||||
|   --ui-color-content-bg: color-mix(in srgb, #{$background} 95%, white 5%); | ||||
|   --ui-color-content-text: #{$text}; | ||||
|   /* UI Library specific CSS variables - HEX format */ | ||||
|   --ui-color-primary: #{$primary-color}; | ||||
|   --ui-color-secondary: #{$secondary-color}; | ||||
|   --ui-color-background: #{$background-color}; | ||||
|   --ui-color-text: #{$text-color}; | ||||
|   --ui-color-border: #{$border-color}; | ||||
|   --ui-color-hover: #{$hover-color}; | ||||
| 
 | ||||
|   --ui-color-icon-primary: #{$primary}; | ||||
|   --ui-color-icon-secondary: #{$secondary}; | ||||
|   --ui-color-icon-muted: color-mix(in srgb, #{$text} 60%, transparent 40%); | ||||
|   /* Derived colors for specific UI components - HEX format */ | ||||
|   --ui-color-menu-bg: #{$background-color}; | ||||
|   --ui-color-menu-text: #{$text-color}; | ||||
|   --ui-color-menu-hover: #{$hover-color}; | ||||
|   --ui-color-menu-active: #{$menu-active-color}; | ||||
|   --ui-color-menu-border: #{$border-color}; | ||||
| 
 | ||||
|   --ui-color-content-bg: #{$content-bg-color}; | ||||
|   --ui-color-content-text: #{$text-color}; | ||||
| 
 | ||||
|   --ui-color-icon-primary: #{$primary-color}; | ||||
|   --ui-color-icon-secondary: #{$secondary-color}; | ||||
|   --ui-color-icon-muted: #{$icon-muted-color}; | ||||
| 
 | ||||
|   /* Card component variables - HEX format */ | ||||
|   --ui-color-card-bg: #{$card-bg-color}; | ||||
|   --ui-color-card-border: #{$border-color}; | ||||
|   --ui-color-card-title: #{$primary-color}; | ||||
|   --ui-color-card-text: #{$card-text-color}; | ||||
|   --ui-color-card-footer-bg: #{$card-footer-bg-color}; | ||||
| 
 | ||||
|   /* Status colors - HEX format */ | ||||
|   --ui-color-success: #{$success-color}; | ||||
|   --ui-color-warning: #{$warning-color}; | ||||
|   --ui-color-danger: #{$danger-color}; | ||||
|   --ui-color-info: #{$info-color}; | ||||
| 
 | ||||
|   /* Contrast colors - automatically choose text or background color for best contrast */ | ||||
|   --ui-color-primary-contrast: #{ui-contrast-color($primary-color, $text-color, $background-color)}; | ||||
|   --ui-color-secondary-contrast: #{ui-contrast-color($secondary-color, $text-color, $background-color)}; | ||||
|   --ui-color-success-contrast: #{ui-contrast-color($success-color, $text-color, $background-color)}; | ||||
|   --ui-color-warning-contrast: #{ui-contrast-color($warning-color, $text-color, $background-color)}; | ||||
|   --ui-color-danger-contrast: #{ui-contrast-color($danger-color, $text-color, $background-color)}; | ||||
|   --ui-color-info-contrast: #{ui-contrast-color($info-color, $text-color, $background-color)}; | ||||
| 
 | ||||
|   /* Hard colors - darker in light themes, lighter in dark themes */ | ||||
|   --ui-color-primary-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $primary-color, 20%), color.mix(white, $primary-color, 20%))}; | ||||
|   --ui-color-secondary-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $secondary-color, 20%), color.mix(white, $secondary-color, 20%))}; | ||||
|   --ui-color-success-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $success-color, 20%), color.mix(white, $success-color, 20%))}; | ||||
|   --ui-color-warning-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $warning-color, 20%), color.mix(white, $warning-color, 20%))}; | ||||
|   --ui-color-danger-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $danger-color, 20%), color.mix(white, $danger-color, 20%))}; | ||||
|   --ui-color-info-hard: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $info-color, 20%), color.mix(white, $info-color, 20%))}; | ||||
| 
 | ||||
|   /* Soft colors - lighter in light themes, darker in dark themes */ | ||||
|   --ui-color-primary-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $primary-color, 20%), color.mix(black, $primary-color, 20%))}; | ||||
|   --ui-color-secondary-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $secondary-color, 20%), color.mix(black, $secondary-color, 20%))}; | ||||
|   --ui-color-success-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $success-color, 20%), color.mix(black, $success-color, 20%))}; | ||||
|   --ui-color-warning-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $warning-color, 20%), color.mix(black, $warning-color, 20%))}; | ||||
|   --ui-color-danger-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $danger-color, 20%), color.mix(black, $danger-color, 20%))}; | ||||
|   --ui-color-info-soft: #{if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $info-color, 20%), color.mix(black, $info-color, 20%))}; | ||||
| 
 | ||||
|   /* RGB versions for rgba() usage - Basic colors */ | ||||
|   --ui-color-primary-rgb: #{color.channel($primary-color, "red")}, #{color.channel($primary-color, "green")}, #{color.channel($primary-color, "blue")}; | ||||
|   --ui-color-secondary-rgb: #{color.channel($secondary-color, "red")}, #{color.channel($secondary-color, "green")}, #{color.channel($secondary-color, "blue")}; | ||||
|   --ui-color-background-rgb: #{color.channel($background-color, "red")}, #{color.channel($background-color, "green")}, #{color.channel($background-color, "blue")}; | ||||
|   --ui-color-text-rgb: #{color.channel($text-color, "red")}, #{color.channel($text-color, "green")}, #{color.channel($text-color, "blue")}; | ||||
|   --ui-color-border-rgb: #{color.channel($border-color, "red")}, #{color.channel($border-color, "green")}, #{color.channel($border-color, "blue")}; | ||||
| 
 | ||||
|   /* Menu colors RGB versions */ | ||||
|   --ui-color-menu-bg-rgb: #{color.channel($background-color, "red")}, #{color.channel($background-color, "green")}, #{color.channel($background-color, "blue")}; | ||||
|   --ui-color-menu-text-rgb: #{color.channel($text-color, "red")}, #{color.channel($text-color, "green")}, #{color.channel($text-color, "blue")}; | ||||
|   --ui-color-menu-border-rgb: #{color.channel($border-color, "red")}, #{color.channel($border-color, "green")}, #{color.channel($border-color, "blue")}; | ||||
| 
 | ||||
|   /* Content colors RGB versions */ | ||||
|   --ui-color-content-text-rgb: #{color.channel($text-color, "red")}, #{color.channel($text-color, "green")}, #{color.channel($text-color, "blue")}; | ||||
|   --ui-color-content-bg-rgb: #{color.channel($content-bg-color, "red")}, #{color.channel($content-bg-color, "green")}, #{color.channel($content-bg-color, "blue")}; | ||||
| 
 | ||||
|   /* Icon colors RGB versions */ | ||||
|   --ui-color-icon-primary-rgb: #{color.channel($primary-color, "red")}, #{color.channel($primary-color, "green")}, #{color.channel($primary-color, "blue")}; | ||||
|   --ui-color-icon-secondary-rgb: #{color.channel($secondary-color, "red")}, #{color.channel($secondary-color, "green")}, #{color.channel($secondary-color, "blue")}; | ||||
| 
 | ||||
|   /* Menu active RGB version */ | ||||
|   --ui-color-menu-active-rgb: #{color.channel($menu-active-color, "red")}, #{color.channel($menu-active-color, "green")}, #{color.channel($menu-active-color, "blue")}; | ||||
| 
 | ||||
|   /* Card colors RGB versions */ | ||||
|   --ui-color-card-border-rgb: #{color.channel($border-color, "red")}, #{color.channel($border-color, "green")}, #{color.channel($border-color, "blue")}; | ||||
|   --ui-color-card-title-rgb: #{color.channel($primary-color, "red")}, #{color.channel($primary-color, "green")}, #{color.channel($primary-color, "blue")}; | ||||
|   --ui-color-card-bg-rgb: #{color.channel($card-bg-color, "red")}, #{color.channel($card-bg-color, "green")}, #{color.channel($card-bg-color, "blue")}; | ||||
|   --ui-color-card-text-rgb: #{color.channel($card-text-color, "red")}, #{color.channel($card-text-color, "green")}, #{color.channel($card-text-color, "blue")}; | ||||
|   --ui-color-card-footer-bg-rgb: #{color.channel($card-footer-bg-color, "red")}, #{color.channel($card-footer-bg-color, "green")}, #{color.channel($card-footer-bg-color, "blue")}; | ||||
| 
 | ||||
|   /* Status colors RGB versions */ | ||||
|   --ui-color-success-rgb: #{color.channel($success-color, "red")}, #{color.channel($success-color, "green")}, #{color.channel($success-color, "blue")}; | ||||
|   --ui-color-warning-rgb: #{color.channel($warning-color, "red")}, #{color.channel($warning-color, "green")}, #{color.channel($warning-color, "blue")}; | ||||
|   --ui-color-danger-rgb: #{color.channel($danger-color, "red")}, #{color.channel($danger-color, "green")}, #{color.channel($danger-color, "blue")}; | ||||
|   --ui-color-info-rgb: #{color.channel($info-color, "red")}, #{color.channel($info-color, "green")}, #{color.channel($info-color, "blue")}; | ||||
| 
 | ||||
|   /* Contrast colors RGB versions */ | ||||
|   --ui-color-primary-contrast-rgb: #{color.channel(ui-contrast-color($primary-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($primary-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($primary-color, $text-color, $background-color), "blue")}; | ||||
|   --ui-color-secondary-contrast-rgb: #{color.channel(ui-contrast-color($secondary-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($secondary-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($secondary-color, $text-color, $background-color), "blue")}; | ||||
|   --ui-color-success-contrast-rgb: #{color.channel(ui-contrast-color($success-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($success-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($success-color, $text-color, $background-color), "blue")}; | ||||
|   --ui-color-warning-contrast-rgb: #{color.channel(ui-contrast-color($warning-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($warning-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($warning-color, $text-color, $background-color), "blue")}; | ||||
|   --ui-color-danger-contrast-rgb: #{color.channel(ui-contrast-color($danger-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($danger-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($danger-color, $text-color, $background-color), "blue")}; | ||||
|   --ui-color-info-contrast-rgb: #{color.channel(ui-contrast-color($info-color, $text-color, $background-color), "red")}, #{color.channel(ui-contrast-color($info-color, $text-color, $background-color), "green")}, #{color.channel(ui-contrast-color($info-color, $text-color, $background-color), "blue")}; | ||||
| 
 | ||||
|   /* Hard colors RGB versions */ | ||||
|   --ui-color-primary-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $primary-color, 20%), color.mix(white, $primary-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $primary-color, 20%), color.mix(white, $primary-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $primary-color, 20%), color.mix(white, $primary-color, 20%)), "blue")}; | ||||
|   --ui-color-secondary-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $secondary-color, 20%), color.mix(white, $secondary-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $secondary-color, 20%), color.mix(white, $secondary-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $secondary-color, 20%), color.mix(white, $secondary-color, 20%)), "blue")}; | ||||
|   --ui-color-success-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $success-color, 20%), color.mix(white, $success-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $success-color, 20%), color.mix(white, $success-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $success-color, 20%), color.mix(white, $success-color, 20%)), "blue")}; | ||||
|   --ui-color-warning-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $warning-color, 20%), color.mix(white, $warning-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $warning-color, 20%), color.mix(white, $warning-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $warning-color, 20%), color.mix(white, $warning-color, 20%)), "blue")}; | ||||
|   --ui-color-danger-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $danger-color, 20%), color.mix(white, $danger-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $danger-color, 20%), color.mix(white, $danger-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $danger-color, 20%), color.mix(white, $danger-color, 20%)), "blue")}; | ||||
|   --ui-color-info-hard-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $info-color, 20%), color.mix(white, $info-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $info-color, 20%), color.mix(white, $info-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(black, $info-color, 20%), color.mix(white, $info-color, 20%)), "blue")}; | ||||
| 
 | ||||
|   /* Soft colors RGB versions */ | ||||
|   --ui-color-primary-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $primary-color, 20%), color.mix(black, $primary-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $primary-color, 20%), color.mix(black, $primary-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $primary-color, 20%), color.mix(black, $primary-color, 20%)), "blue")}; | ||||
|   --ui-color-secondary-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $secondary-color, 20%), color.mix(black, $secondary-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $secondary-color, 20%), color.mix(black, $secondary-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $secondary-color, 20%), color.mix(black, $secondary-color, 20%)), "blue")}; | ||||
|   --ui-color-success-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $success-color, 20%), color.mix(black, $success-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $success-color, 20%), color.mix(black, $success-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $success-color, 20%), color.mix(black, $success-color, 20%)), "blue")}; | ||||
|   --ui-color-warning-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $warning-color, 20%), color.mix(black, $warning-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $warning-color, 20%), color.mix(black, $warning-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $warning-color, 20%), color.mix(black, $warning-color, 20%)), "blue")}; | ||||
|   --ui-color-danger-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $danger-color, 20%), color.mix(black, $danger-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $danger-color, 20%), color.mix(black, $danger-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $danger-color, 20%), color.mix(black, $danger-color, 20%)), "blue")}; | ||||
|   --ui-color-info-soft-rgb: #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $info-color, 20%), color.mix(black, $info-color, 20%)), "red")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $info-color, 20%), color.mix(black, $info-color, 20%)), "green")}, #{color.channel(if(color.channel($background-color, "lightness", $space: hsl) > 50%, color.mix(white, $info-color, 20%), color.mix(black, $info-color, 20%)), "blue")}; | ||||
| 
 | ||||
|   /* Note: hover and icon-muted use rgba(), so they already have alpha transparency built-in */ | ||||
|   /* For hover effects, use the HEX version: var(--ui-color-hover) */ | ||||
|   /* For icon-muted, use the HEX version: var(--ui-color-icon-muted) */ | ||||
| 
 | ||||
|   /* Card spacing and layout variables */ | ||||
|   --ui-spacing-xs: 4px; | ||||
|   --ui-spacing-sm: 8px; | ||||
|   --ui-spacing-md: 16px; | ||||
|   --ui-spacing-lg: 24px; | ||||
|   --ui-border-radius: 8px; | ||||
|   --ui-font-size-lg: 18px; | ||||
|   --ui-font-weight-semibold: 600; | ||||
|   --ui-card-header-min-height: 40px; | ||||
| 
 | ||||
|   /* Card shadows */ | ||||
|   --ui-shadow-card: 0 2px 4px rgba(0, 0, 0, 0.1); | ||||
|   --ui-shadow-card-hover: 0 4px 8px rgba(0, 0, 0, 0.15); | ||||
| 
 | ||||
|   /* Input component variables */ | ||||
|   --ui-color-input-bg: #{color-mix(in srgb, #{$background} 95%, white 5%)}; | ||||
|   --ui-color-input-text: #{color-mix(in srgb, #{$text} 95%, white 5%)}; | ||||
|   --ui-color-input-placeholder: #{color-mix(in srgb, #{$primary} 50%, transparent 50%)}; | ||||
|   --ui-color-input-label-focus: #{color-mix(in srgb, #{$primary} 95%, white 5%)}; | ||||
|   --ui-color-input-label-invalid: #dc3545; | ||||
| 
 | ||||
|   /* Input box-shadow borders and effects */ | ||||
|   --ui-shadow-input-border: 0 0 0 0px #{$primary}, 0 0 0 0px #{if($background == #343a40, #ffffff, color-mix(in srgb, #{$background} 95%, white 5%))}, inset 0 0 0 1px #{$border}; | ||||
|   --ui-shadow-input-focus: 0 0 0 0px #{$primary}, 0 0 0 0px #{if($background == #343a40, #ffffff, color-mix(in srgb, #{$background} 95%, white 5%))}, inset 0 0 0 2px #{$primary}; | ||||
|   --ui-shadow-input-active: 0 0 0 10px #{$primary}, 0 0 0 9px #{if($background == #343a40, #ffffff, color-mix(in srgb, #{$background} 95%, white 5%))}; | ||||
|   --ui-shadow-input-invalid: 0 0 0 0px #{$primary}, 0 0 0 0px #{if($background == #343a40, #ffffff, color-mix(in srgb, #{$background} 95%, white 5%))}, inset 0 0 0 2px #dc3545; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | @ -83,4 +252,31 @@ | |||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Mixin that applies theme-aware styles to card components | ||||
|  */ | ||||
| @mixin ui-card-theme() { | ||||
|   background: var(--ui-color-card-bg); | ||||
|   border: 1px solid var(--ui-color-card-border); | ||||
|   border-radius: var(--ui-border-radius); | ||||
|   box-shadow: var(--ui-shadow-card); | ||||
| 
 | ||||
|   &:hover { | ||||
|     box-shadow: var(--ui-shadow-card-hover); | ||||
|   } | ||||
| 
 | ||||
|   .ui-card-title { | ||||
|     color: var(--ui-color-card-title); | ||||
|   } | ||||
| 
 | ||||
|   .ui-card-content { | ||||
|     color: var(--ui-color-card-text); | ||||
|   } | ||||
| 
 | ||||
|   .ui-card-footer { | ||||
|     background: var(--ui-color-card-footer-bg); | ||||
|     border-top-color: var(--ui-color-card-border); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,150 @@ | |||
| /* Examples of using ui-contrast-color function in UI Library */ | ||||
| 
 | ||||
| @use 'sass:color'; | ||||
| @use 'theme-mixins' as theme; | ||||
| 
 | ||||
| /* Example 1: Basic usage with predefined colors */ | ||||
| .example-button { | ||||
|   $button-bg: #007bff; // Blue background | ||||
|   $light-text: #ffffff; // White text for dark backgrounds | ||||
|   $dark-text: #333333;  // Dark text for light backgrounds | ||||
|    | ||||
|   background-color: $button-bg; | ||||
|   color: theme.ui-contrast-color($button-bg, $light-text, $dark-text); | ||||
|   // Result: white text because blue is darker than 50% lightness | ||||
| } | ||||
| 
 | ||||
| /* Example 2: Using with theme variables */ | ||||
| .example-card { | ||||
|   $card-bg: var(--ui-color-primary); | ||||
|    | ||||
|   // In SCSS context, you need to use actual color values | ||||
|   $primary-color: #2c3e50; // This would come from your theme | ||||
|   $text-light: #ffffff; | ||||
|   $text-dark: #333333; | ||||
|    | ||||
|   background-color: $primary-color; | ||||
|   color: theme.ui-contrast-color($primary-color, $text-light, $text-dark); | ||||
|   // Result: white text because #2c3e50 is dark | ||||
| } | ||||
| 
 | ||||
| /* Example 3: Status colors with automatic contrast */ | ||||
| .status-examples { | ||||
|   .success-badge { | ||||
|     $success: #28a745; // Green | ||||
|     $light: #ffffff; | ||||
|     $dark: #000000; | ||||
|      | ||||
|     background-color: $success; | ||||
|     color: theme.ui-contrast-color($success, $light, $dark); | ||||
|     // Result: white text because green is relatively dark | ||||
|   } | ||||
|    | ||||
|   .warning-badge { | ||||
|     $warning: #ffc107; // Yellow | ||||
|     $light: #ffffff; | ||||
|     $dark: #000000; | ||||
|      | ||||
|     background-color: $warning; | ||||
|     color: theme.ui-contrast-color($warning, $light, $dark); | ||||
|     // Result: black text because yellow is light (>50% lightness) | ||||
|   } | ||||
|    | ||||
|   .danger-badge { | ||||
|     $danger: #dc3545; // Red | ||||
|     $light: #ffffff; | ||||
|     $dark: #000000; | ||||
|      | ||||
|     background-color: $danger; | ||||
|     color: theme.ui-contrast-color($danger, $light, $dark); | ||||
|     // Result: white text because red is dark | ||||
|   } | ||||
|    | ||||
|   .info-badge { | ||||
|     $info: #17a2b8; // Cyan | ||||
|     $light: #ffffff; | ||||
|     $dark: #000000; | ||||
|      | ||||
|     background-color: $info; | ||||
|     color: theme.ui-contrast-color($info, $light, $dark); | ||||
|     // Result: white text because cyan is dark | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /* Example 4: Using with different light/dark colors based on theme */ | ||||
| .theme-aware-component { | ||||
|   // For light theme | ||||
|   $bg-light: #f8f9fa; | ||||
|   $text-light: #333333; | ||||
|   $bg-dark: #343a40; | ||||
|   $text-dark: #ffffff; | ||||
|    | ||||
|   // Component background | ||||
|   $component-bg: #007bff; | ||||
|    | ||||
|   // In light theme context | ||||
|   &.light-theme { | ||||
|     background-color: $component-bg; | ||||
|     color: theme.ui-contrast-color($component-bg, $text-dark, $text-light); | ||||
|     // For light theme: if bg is light, use dark text; if bg is dark, use light text | ||||
|   } | ||||
|    | ||||
|   // In dark theme context | ||||
|   &.dark-theme { | ||||
|     background-color: $component-bg; | ||||
|     color: theme.ui-contrast-color($component-bg, $text-dark, $bg-light); | ||||
|     // For dark theme: if bg is light, use light bg color; if bg is dark, use dark text | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /* Example 5: Dynamic contrast with CSS variables (conceptual) */ | ||||
| /* | ||||
| Note: The ui-contrast-color function works at compile time with SCSS variables. | ||||
| For runtime CSS variables, you would use the pre-computed contrast variables: | ||||
| 
 | ||||
| .dynamic-component { | ||||
|   background-color: var(--ui-color-primary); | ||||
|   color: var(--ui-color-primary-contrast); | ||||
|    | ||||
|   // Or with RGB for transparency: | ||||
|   background-color: rgba(var(--ui-color-primary-rgb), 0.8); | ||||
|   color: var(--ui-color-primary-contrast); | ||||
| } | ||||
| */ | ||||
| 
 | ||||
| /* Example 6: Complex usage with mixed colors */ | ||||
| .complex-example { | ||||
|   $base-colors: ( | ||||
|     'primary': #2c3e50, | ||||
|     'secondary': #6c757d, | ||||
|     'light': #f8f9fa, | ||||
|     'dark': #343a40 | ||||
|   ); | ||||
|    | ||||
|   $text-light: #ffffff; | ||||
|   $text-dark: #333333; | ||||
|    | ||||
|   @each $name, $color in $base-colors { | ||||
|     .#{$name}-variant { | ||||
|       background-color: $color; | ||||
|       color: theme.ui-contrast-color($color, $text-light, $text-dark); | ||||
|        | ||||
|       // Border with semi-transparent contrast color | ||||
|       $contrast: theme.ui-contrast-color($color, $text-light, $text-dark); | ||||
|       border: 2px solid rgba($contrast, 0.3); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /* How the function works: | ||||
|  *  | ||||
|  * ui-contrast-color($base-color, $light-color, $dark-color) | ||||
|  *  | ||||
|  * 1. Calculates lightness of $base-color using HSL color space | ||||
|  * 2. If lightness > 50% (closer to white): returns $dark-color | ||||
|  * 3. If lightness ≤ 50% (closer to black): returns $light-color | ||||
|  *  | ||||
|  * This ensures optimal contrast for readability: | ||||
|  * - Light backgrounds get dark text | ||||
|  * - Dark backgrounds get light text | ||||
|  */ | ||||
|  | @ -17,3 +17,6 @@ export * from './lib/modules/form/components/input/input.component'; | |||
| // Components
 | ||||
| export * from './lib/components/button/button.component'; | ||||
| export * from './lib/components/card/card.component'; | ||||
| export * from './lib/components/card/card-title/card-title.component'; | ||||
| export * from './lib/components/card/card-menu/card-menu.component'; | ||||
| export * from './lib/components/card/card-footer/card-footer.component'; | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue