@foxeltech/angular-ui 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/components.css +2 -17
  2. package/fesm2022/foxeltech-angular-ui.mjs +343 -77
  3. package/fesm2022/foxeltech-angular-ui.mjs.map +1 -1
  4. package/index.d.ts +123 -34
  5. package/package.json +4 -5
  6. package/src/lib/styles/components.css +2 -17
  7. package/src/lib/styles/tailwind.css +11 -8
  8. package/src/lib/styles/tokens.css +131 -0
  9. package/src/lib/styles/utilities.css +24 -0
  10. package/src/lib/ui/components/button/button.component.html +4 -1
  11. package/src/lib/ui/components/checkbox/checkbox.component.html +3 -2
  12. package/src/lib/ui/components/datetime-picker/datetime-picker.component.html +3 -2
  13. package/src/lib/ui/components/datetime-picker/datetime-picker.component.scss +3 -0
  14. package/src/lib/ui/components/drawer/drawer.component.html +3 -0
  15. package/src/lib/ui/components/drawer/drawer.component.scss +3 -0
  16. package/src/lib/ui/components/input/input.component.html +15 -3
  17. package/src/lib/ui/components/kanban-board/kanban-board.component.scss +3 -0
  18. package/src/lib/ui/components/master-detail/master-detail.component.css +11 -0
  19. package/src/lib/ui/components/master-detail/master-detail.component.html +70 -0
  20. package/src/lib/ui/components/meter/meter.component.html +8 -1
  21. package/src/lib/ui/components/radio-button/radio-button.component.html +10 -4
  22. package/src/lib/ui/components/radio-button-toggle/radio-button-toggle.component.html +8 -3
  23. package/src/lib/ui/components/rich-text-area/rich-text-area.component.scss +3 -0
  24. package/src/lib/ui/components/select/select.component.html +9 -4
  25. package/src/lib/ui/components/severity-badges/severity-badges.component.html +5 -4
  26. package/src/lib/ui/components/skeleton-list/skeleton-list.component.html +2 -2
  27. package/src/lib/ui/components/slider/slider.component.html +3 -0
  28. package/src/lib/ui/components/switch/switch.component.html +11 -6
  29. package/src/lib/ui/components/tab-group/tab-group.component.html +15 -1
  30. package/src/lib/ui/components/tab-group/tab-group.component.scss +3 -0
  31. package/src/lib/ui/components/tag/tag.component.html +6 -1
  32. package/src/lib/ui/components/toast/toast.component.html +2 -1
  33. package/src/lib/ui/dialogs/confirmation/confirmation.component.html +1 -1
  34. package/tailwind.css +11 -8
  35. package/tokens.css +131 -0
  36. package/utilities.css +24 -0
  37. package/tailwind-config.d.ts +0 -4
  38. package/tailwind.config.js +0 -113
@@ -1,13 +1,14 @@
1
1
  <div class="flex flex-col mb-4 w-full text-left">
2
2
  @if (label) {
3
- <label class="txt-field-label">
3
+ <label class="txt-field-label" [attr.for]="fieldId">
4
4
  {{ label }}
5
- <span *ngIf="required" class="txt-required">*</span>
5
+ <span *ngIf="required" class="txt-required" aria-hidden="true">*</span>
6
6
  </label>
7
7
  }
8
8
 
9
9
  <div class="relative">
10
10
  <input
11
+ [id]="fieldId"
11
12
  [owlDateTimeTrigger]="picker"
12
13
  [owlDateTime]="picker"
13
14
  [placeholder]="placeholder"
@@ -1,3 +1,6 @@
1
+ /* Tailwind v4: component styles compile in isolation — @reference pulls in the theme for @apply without re-emitting it. */
2
+ @reference '../../../styles/tailwind.css';
3
+
1
4
  // ─── Owl DateTime Picker Custom Styles ───────────────────────────────────────
2
5
 
3
6
  // Container
@@ -7,6 +7,7 @@
7
7
  @if (logo) {
8
8
  <img
9
9
  [src]="logo"
10
+ alt="Home"
10
11
  class="h-[20px] w-auto transition-all duration-300 ease-in-out"
11
12
  [ngClass]="{ 'scale-90': !isExpanded, 'ml-5': isExpanded }"
12
13
  />
@@ -35,6 +36,8 @@
35
36
  <button
36
37
  type="button"
37
38
  class="toggle-btn"
39
+ [attr.aria-label]="isExpanded ? 'Collapse navigation' : 'Expand navigation'"
40
+ [attr.aria-expanded]="isExpanded"
38
41
  (click)="toggleDrawer()"
39
42
  [matTooltip]="isExpanded ? 'Collapse' : 'Expand'"
40
43
  matTooltipClass="text-text-primary text-xs font-semibold"
@@ -1,3 +1,6 @@
1
+ /* Tailwind v4: component styles compile in isolation — @reference pulls in the theme for @apply without re-emitting it. */
2
+ @reference '../../../styles/tailwind.css';
3
+
1
4
  :host {
2
5
  @apply block h-full;
3
6
  }
@@ -1,9 +1,9 @@
1
1
  <div class="mb-4 text-left w-full text-text-primary" [class]="class">
2
2
  @if (label) {
3
- <label class="block txt-field-label">
3
+ <label class="block txt-field-label" [attr.for]="fieldId">
4
4
  {{ label }}
5
5
  @if (required) {
6
- <span class="txt-required">*</span>
6
+ <span class="txt-required" aria-hidden="true">*</span>
7
7
  }
8
8
  @if (tooltip) {
9
9
  <fx-ui-hero-icon
@@ -22,6 +22,7 @@
22
22
  <input
23
23
  appTrimOnBlur
24
24
  [appTrimOnBlurCollapse]="true"
25
+ [id]="fieldId"
25
26
  [type]="displayType"
26
27
  [placeholder]="placeholder"
27
28
  [disabled]="disabled"
@@ -30,6 +31,9 @@
30
31
  (blur)="onBlur()"
31
32
  (focus)="onFocus()"
32
33
  [attr.maxlength]="maxlength"
34
+ [attr.aria-invalid]="invalid || null"
35
+ [attr.aria-describedby]="invalid ? fieldId + '-error' : null"
36
+ [attr.aria-required]="required ? true : null"
33
37
  [class]="type === 'otp' ? 'input-otp' : 'input-default'"
34
38
  [class.input-invalid]="invalid"
35
39
  [class.pr-8]="type === 'password' || suffixIcon"
@@ -41,18 +45,26 @@
41
45
  class="absolute inset-y-0 right-2 flex items-center text-text-primary hover:text-text-secondary"
42
46
  (click)="onSuffixClick()"
43
47
  [disabled]="disabled"
48
+ [attr.aria-label]="
49
+ type === 'password'
50
+ ? (showPassword ? 'Hide password' : 'Show password')
51
+ : (suffixAriaLabel || null)
52
+ "
53
+ [attr.aria-pressed]="type === 'password' ? showPassword : null"
44
54
  >
45
55
  @if (type === 'password') {
46
56
  <fx-ui-hero-icon
47
57
  [icon]="showPassword ? 'eye-slash' : 'eye'"
48
58
  [size]="18"
49
59
  class="text-text-primary"
60
+ aria-hidden="true"
50
61
  ></fx-ui-hero-icon>
51
62
  } @else if (suffixIcon) {
52
63
  <fx-ui-hero-icon
53
64
  [icon]="suffixIcon"
54
65
  [size]="18"
55
66
  class="text-text-primary"
67
+ aria-hidden="true"
56
68
  ></fx-ui-hero-icon>
57
69
  }
58
70
  </button>
@@ -60,7 +72,7 @@
60
72
  </div>
61
73
 
62
74
  @if (invalid) {
63
- <div class="txt-invalid">
75
+ <div class="txt-invalid" role="alert" [id]="fieldId + '-error'">
64
76
  {{ getErrorMessage(label) }}
65
77
  </div>
66
78
  }
@@ -1,3 +1,6 @@
1
+ /* Tailwind v4: component styles compile in isolation — @reference pulls in the theme for @apply without re-emitting it. */
2
+ @reference '../../../styles/tailwind.css';
3
+
1
4
  .kanban-board {
2
5
  @apply w-full h-full overflow-x-auto;
3
6
  }
@@ -0,0 +1,11 @@
1
+ /* Full-width when stacked; fixed width beside the detail pane on lg+ (1080px). */
2
+ .fx-md-list {
3
+ width: 100%;
4
+ }
5
+
6
+ @media (min-width: 1080px) {
7
+ .fx-md-list {
8
+ width: var(--fx-md-list-width, 420px);
9
+ flex-shrink: 0;
10
+ }
11
+ }
@@ -0,0 +1,70 @@
1
+ <div class="flex flex-col lg:flex-row items-start gap-xl" [style.--fx-md-list-width]="listWidth">
2
+ <!-- Master: list pane -->
3
+ <div class="fx-md-list card-common">
4
+ <ng-content select="[mdHeader]"></ng-content>
5
+ <ng-content select="[mdFilters]"></ng-content>
6
+ <div
7
+ class="flex flex-col gap-normal overflow-auto pr-xs"
8
+ [style.maxHeight]="maxListHeight"
9
+ role="listbox"
10
+ [attr.aria-label]="listLabel || null"
11
+ >
12
+ @if (table.loading) {
13
+ <fx-ui-skeleton-list [rows]="table.pageSize"></fx-ui-skeleton-list>
14
+ } @else {
15
+ @for (row of table.dataSource.data; track table.idOf(row); let i = $index) {
16
+ <button
17
+ type="button"
18
+ role="option"
19
+ [id]="optionId(i)"
20
+ [attr.aria-selected]="isSelected(row)"
21
+ [tabindex]="isSelected(row) ? 0 : -1"
22
+ (click)="table.selectItem(row)"
23
+ (keydown)="onKeydown($event, i)"
24
+ class="text-left rounded-lg border p-semi transition-colors"
25
+ [ngClass]="
26
+ isSelected(row)
27
+ ? 'border-gradient-primary-start bg-gradient-primary-start/5'
28
+ : 'border-border-default hover:border-border-interactive bg-bg-primary'
29
+ "
30
+ >
31
+ <ng-container
32
+ [ngTemplateOutlet]="rowTemplate"
33
+ [ngTemplateOutletContext]="{ $implicit: row, selected: isSelected(row) }"
34
+ ></ng-container>
35
+ </button>
36
+ }
37
+ @if (!table.dataSource.data.length) {
38
+ <fx-ui-empty-state [message]="emptyMessage"></fx-ui-empty-state>
39
+ }
40
+ }
41
+ </div>
42
+ <!-- No (page) binding: ngAfterViewInit hands this paginator to the table,
43
+ whose own BaseTable subscription (running after this child's hook)
44
+ wires page events — a binding here would double-fetch every change. -->
45
+ <mat-paginator
46
+ class="mt-normal"
47
+ [length]="table.total"
48
+ [pageSize]="table.pageSize"
49
+ [pageSizeOptions]="pageSizeOptions"
50
+ ></mat-paginator>
51
+ </div>
52
+
53
+ <!-- Detail pane -->
54
+ <div class="w-full lg:flex-1 min-w-0">
55
+ @if (table.selected) {
56
+ @if (table.detailBusy) {
57
+ <fx-ui-skeleton-detail [cards]="skeletonCards"></fx-ui-skeleton-detail>
58
+ } @else {
59
+ <ng-container
60
+ [ngTemplateOutlet]="detailTemplate"
61
+ [ngTemplateOutletContext]="{ $implicit: table.detail, selected: table.selected }"
62
+ ></ng-container>
63
+ }
64
+ } @else {
65
+ <div class="card-common flex items-center justify-center h-[300px]">
66
+ <fx-ui-empty-state [message]="emptyMessage"></fx-ui-empty-state>
67
+ </div>
68
+ }
69
+ </div>
70
+ </div>
@@ -9,7 +9,14 @@
9
9
  }
10
10
  </div>
11
11
  }
12
- <div class="h-2 rounded-full bg-bg-hover overflow-hidden">
12
+ <div
13
+ class="h-2 rounded-full bg-bg-hover overflow-hidden"
14
+ role="progressbar"
15
+ [attr.aria-valuemin]="0"
16
+ [attr.aria-valuemax]="max"
17
+ [attr.aria-valuenow]="value"
18
+ [attr.aria-label]="label || null"
19
+ >
13
20
  <div class="h-full rounded-full" [ngClass]="fillClass" [style.width.%]="percent"></div>
14
21
  </div>
15
22
  </div>
@@ -1,9 +1,15 @@
1
- <div class="w-full rounded-lg overflow-hidden" [class]="class" role="radiogroup">
1
+ <div
2
+ class="w-full rounded-lg overflow-hidden"
3
+ [class]="class"
4
+ role="radiogroup"
5
+ [attr.aria-labelledby]="label ? groupId + '-label' : null"
6
+ >
7
+ <!-- A radiogroup has no single control a <label> could point at — name it via aria-labelledby. -->
2
8
  @if (label) {
3
- <label class="txt-field-label">
9
+ <span class="txt-field-label block" [id]="groupId + '-label'">
4
10
  {{ label }}
5
11
  @if (required) {
6
- <span class="txt-required">*</span>
12
+ <span class="txt-required" aria-hidden="true">*</span>
7
13
  }
8
14
  @if (tooltip) {
9
15
  <fx-ui-hero-icon
@@ -15,7 +21,7 @@
15
21
  matTooltipClass="text-text-primary text-xs font-semibold"
16
22
  ></fx-ui-hero-icon>
17
23
  }
18
- </label>
24
+ </span>
19
25
  }
20
26
 
21
27
  <button
@@ -1,10 +1,15 @@
1
1
  <div class="flex flex-col text-left mb-4 w-full" [class]="class">
2
- @if(label){<label class="txt-field-label">
2
+ <!-- A radiogroup has no single control a <label> could point at — name it via aria-labelledby. -->
3
+ @if(label){<span class="txt-field-label" [id]="groupId + '-label'">
3
4
  {{ label }}
4
- @if(required) {<span class="txt-required">*</span>} </label
5
+ @if(required) {<span class="txt-required" aria-hidden="true">*</span>} </span
5
6
  >}
6
7
 
7
- <div class="inline-flex w-full rounded-lg overflow-hidden" role="radiogroup">
8
+ <div
9
+ class="inline-flex w-full rounded-lg overflow-hidden"
10
+ role="radiogroup"
11
+ [attr.aria-labelledby]="label ? groupId + '-label' : null"
12
+ >
8
13
  @for(opt of options; track opt){<button
9
14
  type="button"
10
15
  role="radio"
@@ -1,3 +1,6 @@
1
+ /* Tailwind v4: component styles compile in isolation — @reference pulls in the theme for @apply without re-emitting it. */
2
+ @reference '../../../styles/tailwind.css';
3
+
1
4
  :host {
2
5
  display: block;
3
6
  }
@@ -1,9 +1,9 @@
1
1
  <div class="mb-4 text-left w-full" [class]="class">
2
2
  @if (label) {
3
- <label class="block txt-field-label">
3
+ <label class="block txt-field-label" [attr.for]="fieldId">
4
4
  {{ label }}
5
5
  @if (required) {
6
- <span class="txt-required">*</span>
6
+ <span class="txt-required" aria-hidden="true">*</span>
7
7
  }
8
8
  @if (tooltip) {
9
9
  <fx-ui-hero-icon
@@ -24,6 +24,10 @@
24
24
  appearance="fill"
25
25
  >
26
26
  <mat-select
27
+ [id]="fieldId"
28
+ [required]="!!required"
29
+ [attr.aria-invalid]="invalid || null"
30
+ [attr.aria-describedby]="invalid ? fieldId + '-error' : null"
27
31
  [placeholder]="placeholder"
28
32
  [disabled]="disabled"
29
33
  [multiple]="multiple"
@@ -36,11 +40,12 @@
36
40
  <div class="px-semi w-full">
37
41
  <input
38
42
  type="text"
43
+ aria-label="Search"
39
44
  [placeholder]="'Search'"
40
45
  [(ngModel)]="searchTerm"
41
46
  (ngModelChange)="onSearchChange($event)"
42
47
  (click)="$event.stopPropagation()"
43
- class="text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out"
48
+ class="text-text-primary placeholder:text-text-placeholder w-full bg-bg-primary border-b px-semi py-normal border-border-default focus:outline-none focus:border-gradient-primary-start focus-visible:ring-2 focus-visible:ring-primary/50 text-sm text-text-primary tracking-normal ring-0 transition-colors duration-500 ease-in-out"
44
49
  />
45
50
  </div>
46
51
  }
@@ -51,7 +56,7 @@
51
56
  </mat-form-field>
52
57
 
53
58
  @if (invalid) {
54
- <div class="txt-invalid text-left">
59
+ <div class="txt-invalid text-left" role="alert" [id]="fieldId + '-error'">
55
60
  {{ getErrorMessage(label) }}
56
61
  </div>
57
62
  }
@@ -1,10 +1,11 @@
1
1
  <div class="flex items-center gap-small">
2
2
  @for (badge of badges; track badge.key) {
3
- <span
4
- class="px-2 py-0.5 rounded-full txt-utility-caption font-medium cursor-pointer"
3
+ <button
4
+ type="button"
5
+ class="px-2 py-0.5 rounded-full txt-utility-caption font-medium cursor-pointer focus-visible:ring-2 focus-visible:ring-primary/50"
5
6
  [ngClass]="badgeClass(badge)"
7
+ [attr.aria-label]="badge.key + ': ' + badge.label"
6
8
  (click)="badgeClick.emit(badge.key)"
7
- >{{ badge.label }}</span
8
- >
9
+ >{{ badge.label }}</button>
9
10
  }
10
11
  </div>
@@ -1,8 +1,8 @@
1
1
  <div class="flex flex-col gap-normal animate-pulse">
2
2
  @for (r of range(rows); track r) {
3
- <div class="rounded-normal border border-border-default p-semi">
3
+ <div class="rounded-lg border border-border-default p-semi">
4
4
  <div class="flex items-center gap-normal mb-small">
5
- <div class="w-8 h-8 rounded-normal bg-bg-hover shrink-0"></div>
5
+ <div class="w-8 h-8 rounded-lg bg-bg-hover shrink-0"></div>
6
6
  <div class="h-4 flex-1 rounded bg-bg-hover"></div>
7
7
  <div class="h-5 w-12 rounded-full bg-bg-hover shrink-0"></div>
8
8
  </div>
@@ -4,6 +4,8 @@
4
4
  [class.opacity-60]="disabled"
5
5
  role="group"
6
6
  >
7
+ <!-- Track click is a pointer shortcut; the thumb button below owns keyboard/focus. -->
8
+ <!-- eslint-disable-next-line @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->
7
9
  <div
8
10
  #track
9
11
  class="relative touch-none py-4"
@@ -38,6 +40,7 @@
38
40
  class="absolute -mt-1 -ml-1.5 w-4 h-4 rounded-full shadow focus:ring-primary bg-primary border-2 border-text-primary"
39
41
  [ngStyle]="thumbStyle()"
40
42
  role="slider"
43
+ aria-label="Slider"
41
44
  [attr.aria-valuemin]="min"
42
45
  [attr.aria-valuemax]="max"
43
46
  [attr.aria-valuenow]="value"
@@ -1,5 +1,10 @@
1
- <div
2
- class="relative flex items-center w-10 h-5 rounded-full transition-colors duration-300 cursor-pointer"
1
+ <button
2
+ type="button"
3
+ role="switch"
4
+ [attr.aria-checked]="!!value"
5
+ [attr.aria-label]="ariaLabel || null"
6
+ [disabled]="disabled"
7
+ class="relative flex items-center w-10 h-5 rounded-full transition-colors duration-300 cursor-pointer disabled:cursor-not-allowed focus-visible:ring-2 focus-visible:ring-primary/50"
3
8
  [ngClass]="{
4
9
  'bg-success': value && !disabled,
5
10
  'bg-button-default': !value && !disabled,
@@ -8,14 +13,14 @@
8
13
  (click)="toggle()"
9
14
  >
10
15
  @if(value) {
11
- <i style="font-size: 20px;" class="material-symbols-outlined absolute left-0.5 top-0 text-text-inverse">check</i>
12
-
16
+ <i style="font-size: 20px;" class="material-symbols-outlined absolute left-0.5 top-0 text-text-inverse" aria-hidden="true">check</i>
17
+
13
18
  }
14
19
  @if(!value) {
15
- <i style="font-size: 20px;" class="material-symbols-outlined absolute right-0.5 top-0 text-text-inverse">close</i>
20
+ <i style="font-size: 20px;" class="material-symbols-outlined absolute right-0.5 top-0 text-text-inverse" aria-hidden="true">close</i>
16
21
  }
17
22
  <div
18
23
  class="absolute top-0.5 left-0.5 w-4 h-4 bg-text-inverse rounded-full shadow-md transform transition-transform duration-300"
19
24
  [ngClass]="{ 'translate-x-5': value }"
20
25
  ></div>
21
- </div>
26
+ </button>
@@ -3,11 +3,15 @@
3
3
  <button
4
4
  class="left-scroll-button"
5
5
  type="button"
6
+ aria-label="Scroll tabs left"
6
7
  (click)="scroll('left')"
7
8
  [style.opacity]="canScrollLeft ? 1 : 0"
8
9
  [style.pointerEvents]="canScrollLeft ? 'auto' : 'none'"
10
+ [attr.tabindex]="canScrollLeft ? null : -1"
11
+ [attr.aria-hidden]="!canScrollLeft"
9
12
  >
10
13
  <svg
14
+ aria-hidden="true"
11
15
  xmlns="http://www.w3.org/2000/svg"
12
16
  class="h-5 w-5 text-text-primary"
13
17
  fill="none"
@@ -20,12 +24,18 @@
20
24
 
21
25
  <div
22
26
  #tabList
27
+ role="tablist"
23
28
  class="relative flex overflow-x-auto no-scrollbar scroll-smooth"
24
29
  (scroll)="onScroll()"
25
30
  >
26
31
  <button
27
32
  *ngFor="let tab of tabs.toArray(); let i = index"
33
+ type="button"
34
+ role="tab"
35
+ [attr.aria-selected]="i === activeIndex"
36
+ [tabindex]="i === activeIndex ? 0 : -1"
28
37
  (click)="selectTab(i)"
38
+ (keydown)="onTabKeydown($event, i)"
29
39
  class="tab-btn tab-bar-btn"
30
40
  >
31
41
  <div class="flex flex-row items-center justify-center">
@@ -56,11 +66,15 @@
56
66
  <button
57
67
  class="right-scroll-button"
58
68
  type="button"
69
+ aria-label="Scroll tabs right"
59
70
  (click)="scroll('right')"
60
71
  [style.opacity]="canScrollRight ? 1 : 0"
61
72
  [style.pointerEvents]="canScrollRight ? 'auto' : 'none'"
73
+ [attr.tabindex]="canScrollRight ? null : -1"
74
+ [attr.aria-hidden]="!canScrollRight"
62
75
  >
63
76
  <svg
77
+ aria-hidden="true"
64
78
  xmlns="http://www.w3.org/2000/svg"
65
79
  class="h-5 w-5 text-text-primary"
66
80
  fill="none"
@@ -75,7 +89,7 @@
75
89
  <!-- Tab content -->
76
90
  <div class="transition-all duration-300 ease-in-out mt-4">
77
91
  <ng-container *ngFor="let tab of tabs.toArray(); let i = index">
78
- <div *ngIf="i === activeIndex" class="animate-fadeIn">
92
+ <div *ngIf="i === activeIndex" role="tabpanel" class="animate-fadeIn">
79
93
  <ng-container *ngTemplateOutlet="tab.content"></ng-container>
80
94
  </div>
81
95
  </ng-container>
@@ -1,3 +1,6 @@
1
+ /* Tailwind v4: component styles compile in isolation — @reference pulls in the theme for @apply without re-emitting it. */
2
+ @reference '../../../styles/tailwind.css';
3
+
1
4
  @keyframes fadeIn {
2
5
  from {
3
6
  opacity: 0;
@@ -1,7 +1,12 @@
1
+ <!-- Interactive semantics only when a (clicked) listener is attached. -->
1
2
  <div
2
3
  (click)="onClick()"
4
+ (keydown.enter)="onClick()"
5
+ (keydown.space)="$event.preventDefault(); onClick()"
6
+ [attr.role]="clicked.observed ? 'button' : null"
7
+ [attr.tabindex]="clicked.observed ? 0 : null"
3
8
  [class]="
4
- `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}`
9
+ `flex flex-row gap-small ${rounded ? 'tag-round' : 'tag-square'} tag-${type}${solid ? '-solid' : ''}${clicked.observed ? ' cursor-pointer focus-visible:ring-2 focus-visible:ring-primary/50' : ''}`
5
10
  "
6
11
  >
7
12
  @if (icon) {
@@ -1,6 +1,7 @@
1
1
  <div
2
2
  animate.enter="horizontal-fade-in-animation"
3
3
  animate.leave="horizontal-fade-out-animation"
4
+ [attr.role]="type === 'error' ? 'alert' : 'status'"
4
5
  class="flex border-l-[3px] items-center bg-bg-primary gap-semi min-w-[250px] max-w-[400px] px-large py-semi rounded shadow-lg transition-all duration-300 ease-in-out transform opacity-100 translate-y-0"
5
6
  [ngClass]="{
6
7
  'border-success': type === 'success',
@@ -9,7 +10,7 @@
9
10
  'border-information': type === 'info'
10
11
  }"
11
12
  >
12
- <div class="">
13
+ <div class="" aria-hidden="true">
13
14
  @switch (type) { @case('success') {
14
15
  <fx-ui-hero-icon
15
16
  icon="check-circle"
@@ -1,5 +1,5 @@
1
1
  <div animate.enter="fade-in-animation" class="dialog-container">
2
- <h1 class="txt-heading-05 dialog-header">{{ title }}</h1>
2
+ <h1 mat-dialog-title class="txt-heading-05 dialog-header">{{ title }}</h1>
3
3
  <mat-dialog-content>
4
4
  <div class="txt-default">{{ message }}</div>
5
5
  </mat-dialog-content>
package/tailwind.css CHANGED
@@ -1,18 +1,21 @@
1
+ /* fonts.css is NOT imported here: Tailwind v4 inlines imports and rebases
2
+ font url()s against the entry file, which breaks webpack resolution.
3
+ Consumers add '@foxeltech/angular-ui/fonts.css' to their styles array. */
4
+ @import 'tailwindcss';
5
+ @import './tokens.css';
6
+ @import './utilities.css';
1
7
  @import './theme.css';
2
- @import './components.css';
3
- @import './dialogs.css';
4
- @import './fonts.css';
8
+ @import './components.css' layer(components);
9
+ @import './dialogs.css' layer(components);
5
10
  @import './animations.css';
6
11
  @import '@danielmoncada/angular-datetime-picker/assets/style/picker.min.css';
7
- @tailwind base;
8
- @tailwind components;
9
- @tailwind utilities;
10
12
 
11
- html, body {
13
+ html,
14
+ body {
12
15
  font-family: 'Roboto';
13
16
  margin: 0;
14
17
  padding: 0;
15
18
  height: 100%;
16
19
  width: 100%;
17
20
  background: rgb(var(--bg-primary));
18
- }
21
+ }