semmet-angular 0.2.0 → 0.4.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.
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # semmet-angular
2
2
 
3
- **Real Angular components, same ARIA vocabulary as [Semmet](https://github.com/daniloagostinho/semmet).**
3
+ **Angular schematics that generate real, fully working, accessible components.**
4
4
 
5
- `ng generate semmet-angular:accordion my-faq` scaffolds a standalone, signals-based Angular component — `.ts`/`.html`/`.css` — that implements the W3C ARIA Authoring Practices Guide pattern for real: correct roles and `aria-*` wiring, **and** the keyboard/focus behavior the pattern requires (arrow-key navigation, focus traps, focus restoration...). Unlike Semmet (markup only, by design), here markup alone isn't enough — so the generated component is fully self-contained and functional out of the box.
5
+ `ng generate semmet-angular:accordion my-faq` scaffolds a standalone, signals-based Angular component — `.ts`/`.html`/`.css` — that implements a W3C ARIA Authoring Practices Guide pattern for real: correct roles and `aria-*` wiring, the keyboard/focus behavior the pattern requires (arrow-key navigation, focus traps, focus restoration...), and a consistent visual identity out of the box.
6
6
 
7
- ## Why a separate project from Semmet
7
+ ## Relationship to Semmet
8
8
 
9
- Semmet's whole pitch is "just markup, zero CSS/JS, framework-agnostic" that's the opposite of what's needed here. Generating a real Angular component means real state, real event handlers, real styling. Same ARIA vocabulary as the source of truth, different product.
9
+ [Semmet](https://github.com/daniloagostinho/semmet) is a VS Code extension that autocompletes the same W3C ARIA vocabulary as plain HTML — no CSS, no JS, framework-agnostic by design. semmet-angular draws from that same vocabulary as its source of truth, but takes the opposite approach on purpose: an Angular component isn't usable with markup alone, so everything generated here — state, event handlers, styling is fully working from the start, not just structure.
10
10
 
11
11
  ## Usage
12
12
 
@@ -35,6 +35,10 @@ Each generated component is dropped into your project exactly like `ng generate
35
35
  - No `.component` suffix on file names or class names (`accordion.ts`, `export class Accordion`), matching the current `ng generate component` convention.
36
36
  - A unique per-instance id (`<name>-0`, `<name>-1`, ...) so multiple instances of the same generated component never collide on `id`/`aria-controls`/`aria-labelledby`.
37
37
 
38
+ ## Visual identity
39
+
40
+ Every generated component shares the same Material Design-informed look — clean surfaces, subtle elevation, a consistent radius/motion scale — using its own color palette (not Material's colors, to stay clear of any lookalike/plagiarism concern). The tokens are plain CSS custom properties declared on each component's `:host` (e.g. `--semmet-color-primary`, `--semmet-radius-md`, `--semmet-elevation-1`), so you can either use the defaults as-is or override the whole family from a global stylesheet without touching the generated files.
41
+
38
42
  ## Options
39
43
 
40
44
  Every schematic accepts the same three options (same as `ng generate component`):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "semmet-angular",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Angular schematics that generate real, ARIA-conformant standalone components (Accordion, Tabs, Dialog, Disclosure, Tooltip...) with signals-based state and keyboard interaction built in.",
5
5
  "publisher": "danilodevsilva",
6
6
  "license": "MIT",
@@ -1,31 +1,75 @@
1
1
  :host {
2
+ --semmet-color-primary: #0d7a72;
3
+ --semmet-color-surface: #ffffff;
4
+ --semmet-color-outline: #d7dedd;
5
+ --semmet-color-on-surface: #1b201f;
6
+ --semmet-color-on-surface-variant: #576260;
7
+ --semmet-radius-md: 12px;
8
+ --semmet-elevation-1: 0 1px 2px rgba(20, 24, 23, 0.1), 0 1px 1px rgba(20, 24, 23, 0.06);
9
+ --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
10
+
2
11
  display: block;
3
- font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
12
+ font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
13
+ font-size: 0.9375rem;
14
+ line-height: 1.5;
15
+ color: var(--semmet-color-on-surface);
16
+ background: var(--semmet-color-surface);
17
+ border: 1px solid var(--semmet-color-outline);
18
+ border-radius: var(--semmet-radius-md);
19
+ box-shadow: var(--semmet-elevation-1);
20
+ overflow: hidden;
4
21
  }
5
22
 
6
23
  h3 {
7
24
  margin: 0;
8
25
  }
9
26
 
27
+ h3:not(:first-of-type) {
28
+ border-top: 1px solid var(--semmet-color-outline);
29
+ }
30
+
10
31
  button {
11
32
  display: flex;
12
33
  align-items: center;
34
+ gap: 0.75rem;
13
35
  width: 100%;
14
- padding: 0.75rem 1rem;
36
+ padding: 0.875rem 1.125rem;
15
37
  font: inherit;
38
+ font-weight: 500;
16
39
  color: inherit;
17
40
  background: none;
18
- border: 1px solid currentColor;
19
- border-radius: 6px;
41
+ border: none;
20
42
  cursor: pointer;
21
43
  text-align: left;
44
+ transition: background-color var(--semmet-motion);
45
+ }
46
+
47
+ button:hover {
48
+ background: color-mix(in srgb, var(--semmet-color-on-surface) 6%, transparent);
22
49
  }
23
50
 
24
51
  button:focus-visible {
25
- outline: 2px solid currentColor;
26
- outline-offset: 2px;
52
+ outline: 2px solid var(--semmet-color-primary);
53
+ outline-offset: -2px;
54
+ }
55
+
56
+ button::after {
57
+ content: '';
58
+ width: 0.5rem;
59
+ height: 0.5rem;
60
+ margin-left: auto;
61
+ flex-shrink: 0;
62
+ border-right: 2px solid var(--semmet-color-on-surface-variant);
63
+ border-bottom: 2px solid var(--semmet-color-on-surface-variant);
64
+ transform: rotate(45deg);
65
+ transition: transform var(--semmet-motion);
66
+ }
67
+
68
+ button[aria-expanded='true']::after {
69
+ transform: rotate(-135deg);
27
70
  }
28
71
 
29
72
  [role='region'] {
30
- padding: 0.75rem 1rem;
73
+ padding: 0 1.125rem 1.125rem;
74
+ color: var(--semmet-color-on-surface-variant);
31
75
  }
@@ -1,11 +1,43 @@
1
1
  :host {
2
- font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
2
+ --semmet-color-primary: #0d7a72;
3
+ --semmet-color-surface: #ffffff;
4
+ --semmet-color-on-surface: #1b201f;
5
+ --semmet-color-on-surface-variant: #576260;
6
+ --semmet-color-scrim: rgba(20, 24, 23, 0.45);
7
+ --semmet-radius-lg: 20px;
8
+ --semmet-elevation-3: 0 8px 24px rgba(20, 24, 23, 0.18), 0 2px 8px rgba(20, 24, 23, 0.1);
9
+ --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
10
+
11
+ font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
12
+ font-size: 0.9375rem;
13
+ line-height: 1.5;
14
+ }
15
+
16
+ button {
17
+ font: inherit;
18
+ font-weight: 500;
19
+ color: var(--semmet-color-primary);
20
+ background: none;
21
+ border: none;
22
+ border-radius: 8px;
23
+ padding: 0.5rem 1rem;
24
+ cursor: pointer;
25
+ transition: background-color var(--semmet-motion);
26
+ }
27
+
28
+ button:hover {
29
+ background: color-mix(in srgb, var(--semmet-color-primary) 8%, transparent);
30
+ }
31
+
32
+ button:focus-visible {
33
+ outline: 2px solid var(--semmet-color-primary);
34
+ outline-offset: 2px;
3
35
  }
4
36
 
5
37
  .backdrop {
6
38
  position: fixed;
7
39
  inset: 0;
8
- background: rgb(0 0 0 / 50%);
40
+ background: var(--semmet-color-scrim);
9
41
  }
10
42
 
11
43
  [role='dialog'] {
@@ -13,20 +45,27 @@
13
45
  top: 50%;
14
46
  left: 50%;
15
47
  transform: translate(-50%, -50%);
16
- min-width: 20rem;
48
+ min-width: 22rem;
17
49
  max-width: 90vw;
18
50
  padding: 1.5rem;
19
- border-radius: 8px;
20
- background: canvas;
21
- color: canvastext;
22
- box-shadow: 0 10px 30px rgb(0 0 0 / 25%);
51
+ border-radius: var(--semmet-radius-lg);
52
+ background: var(--semmet-color-surface);
53
+ color: var(--semmet-color-on-surface);
54
+ box-shadow: var(--semmet-elevation-3);
23
55
  }
24
56
 
25
57
  [role='dialog']:focus-visible {
26
- outline: 2px solid currentColor;
58
+ outline: 2px solid var(--semmet-color-primary);
27
59
  outline-offset: 2px;
28
60
  }
29
61
 
30
- button {
31
- font: inherit;
62
+ [role='dialog'] h2 {
63
+ margin: 0 0 0.75rem;
64
+ font-size: 1.25rem;
65
+ font-weight: 500;
66
+ }
67
+
68
+ [role='dialog'] > div {
69
+ margin-bottom: 1.25rem;
70
+ color: var(--semmet-color-on-surface-variant);
32
71
  }
@@ -1,27 +1,67 @@
1
1
  :host {
2
+ --semmet-color-primary: #0d7a72;
3
+ --semmet-color-surface: #ffffff;
4
+ --semmet-color-outline: #d7dedd;
5
+ --semmet-color-on-surface: #1b201f;
6
+ --semmet-color-on-surface-variant: #576260;
7
+ --semmet-radius-md: 12px;
8
+ --semmet-elevation-1: 0 1px 2px rgba(20, 24, 23, 0.1), 0 1px 1px rgba(20, 24, 23, 0.06);
9
+ --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
10
+
2
11
  display: block;
3
- font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
12
+ font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
13
+ font-size: 0.9375rem;
14
+ line-height: 1.5;
15
+ color: var(--semmet-color-on-surface);
16
+ background: var(--semmet-color-surface);
17
+ border: 1px solid var(--semmet-color-outline);
18
+ border-radius: var(--semmet-radius-md);
19
+ box-shadow: var(--semmet-elevation-1);
20
+ overflow: hidden;
4
21
  }
5
22
 
6
23
  button {
7
24
  display: flex;
8
25
  align-items: center;
26
+ gap: 0.75rem;
9
27
  width: 100%;
10
- padding: 0.75rem 1rem;
28
+ padding: 0.875rem 1.125rem;
11
29
  font: inherit;
30
+ font-weight: 500;
12
31
  color: inherit;
13
32
  background: none;
14
- border: 1px solid currentColor;
15
- border-radius: 6px;
33
+ border: none;
16
34
  cursor: pointer;
17
35
  text-align: left;
36
+ transition: background-color var(--semmet-motion);
37
+ }
38
+
39
+ button:hover {
40
+ background: color-mix(in srgb, var(--semmet-color-on-surface) 6%, transparent);
18
41
  }
19
42
 
20
43
  button:focus-visible {
21
- outline: 2px solid currentColor;
22
- outline-offset: 2px;
44
+ outline: 2px solid var(--semmet-color-primary);
45
+ outline-offset: -2px;
46
+ }
47
+
48
+ button::after {
49
+ content: '';
50
+ width: 0.5rem;
51
+ height: 0.5rem;
52
+ margin-left: auto;
53
+ flex-shrink: 0;
54
+ border-right: 2px solid var(--semmet-color-on-surface-variant);
55
+ border-bottom: 2px solid var(--semmet-color-on-surface-variant);
56
+ transform: rotate(45deg);
57
+ transition: transform var(--semmet-motion);
58
+ }
59
+
60
+ button[aria-expanded='true']::after {
61
+ transform: rotate(-135deg);
23
62
  }
24
63
 
25
64
  div {
26
- padding: 0.75rem 1rem;
65
+ padding: 0 1.125rem 1.125rem;
66
+ color: var(--semmet-color-on-surface-variant);
27
67
  }
@@ -1,39 +1,73 @@
1
1
  :host {
2
+ --semmet-color-primary: #0d7a72;
3
+ --semmet-color-outline: #d7dedd;
4
+ --semmet-color-on-surface: #1b201f;
5
+ --semmet-color-on-surface-variant: #576260;
6
+ --semmet-radius-sm: 8px;
7
+ --semmet-motion: 160ms cubic-bezier(0.2, 0, 0, 1);
8
+
2
9
  display: block;
3
- font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
10
+ font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
11
+ font-size: 0.9375rem;
12
+ line-height: 1.5;
13
+ color: var(--semmet-color-on-surface);
4
14
  }
5
15
 
6
16
  [role='tablist'] {
7
17
  display: flex;
8
18
  gap: 0.25rem;
9
- border-bottom: 1px solid currentColor;
19
+ border-bottom: 1px solid var(--semmet-color-outline);
10
20
  }
11
21
 
12
22
  [role='tab'] {
23
+ position: relative;
13
24
  font: inherit;
14
- color: inherit;
25
+ font-weight: 500;
26
+ color: var(--semmet-color-on-surface-variant);
15
27
  background: none;
16
28
  border: none;
17
- padding: 0.6rem 1rem;
29
+ border-radius: var(--semmet-radius-sm) var(--semmet-radius-sm) 0 0;
30
+ padding: 0.75rem 1.125rem;
18
31
  cursor: pointer;
19
- border-bottom: 2px solid transparent;
32
+ transition: background-color var(--semmet-motion), color var(--semmet-motion);
20
33
  }
21
34
 
22
- [role='tab'][aria-selected='true'] {
23
- border-bottom-color: currentColor;
24
- font-weight: 600;
35
+ [role='tab']:hover {
36
+ background: color-mix(in srgb, var(--semmet-color-on-surface) 6%, transparent);
25
37
  }
26
38
 
27
39
  [role='tab']:focus-visible {
28
- outline: 2px solid currentColor;
40
+ outline: 2px solid var(--semmet-color-primary);
29
41
  outline-offset: 2px;
30
42
  }
31
43
 
44
+ [role='tab']::after {
45
+ content: '';
46
+ position: absolute;
47
+ left: 0.5rem;
48
+ right: 0.5rem;
49
+ bottom: -1px;
50
+ height: 2px;
51
+ border-radius: 2px 2px 0 0;
52
+ background: var(--semmet-color-primary);
53
+ transform: scaleX(0);
54
+ transition: transform var(--semmet-motion);
55
+ }
56
+
57
+ [role='tab'][aria-selected='true'] {
58
+ color: var(--semmet-color-primary);
59
+ }
60
+
61
+ [role='tab'][aria-selected='true']::after {
62
+ transform: scaleX(1);
63
+ }
64
+
32
65
  [role='tabpanel'] {
33
- padding: 1rem 0;
66
+ padding: 1.125rem 0.25rem;
67
+ color: var(--semmet-color-on-surface-variant);
34
68
  }
35
69
 
36
70
  [role='tabpanel']:focus-visible {
37
- outline: 2px solid currentColor;
71
+ outline: 2px solid var(--semmet-color-primary);
38
72
  outline-offset: 2px;
39
73
  }
@@ -1,21 +1,35 @@
1
1
  :host {
2
+ --semmet-color-primary: #0d7a72;
3
+ --semmet-color-inverse-surface: #2f3736;
4
+ --semmet-color-inverse-on-surface: #ffffff;
5
+ --semmet-radius-sm: 6px;
6
+ --semmet-elevation-1: 0 1px 2px rgba(20, 24, 23, 0.1), 0 1px 1px rgba(20, 24, 23, 0.06);
7
+ --semmet-motion: 120ms cubic-bezier(0.2, 0, 0, 1);
8
+
2
9
  position: relative;
3
10
  display: inline-block;
4
- font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
11
+ font-family: Roboto, system-ui, -apple-system, 'Segoe UI', sans-serif;
12
+ font-size: 0.9375rem;
5
13
  }
6
14
 
7
15
  button {
8
16
  font: inherit;
9
- color: inherit;
17
+ font-weight: 500;
18
+ color: var(--semmet-color-primary);
10
19
  background: none;
11
20
  border: 1px solid currentColor;
12
- border-radius: 6px;
13
- padding: 0.4rem 0.75rem;
21
+ border-radius: var(--semmet-radius-sm);
22
+ padding: 0.5rem 1rem;
14
23
  cursor: pointer;
24
+ transition: background-color var(--semmet-motion);
25
+ }
26
+
27
+ button:hover {
28
+ background: color-mix(in srgb, var(--semmet-color-primary) 8%, transparent);
15
29
  }
16
30
 
17
31
  button:focus-visible {
18
- outline: 2px solid currentColor;
32
+ outline: 2px solid var(--semmet-color-primary);
19
33
  outline-offset: 2px;
20
34
  }
21
35
 
@@ -23,11 +37,13 @@ button:focus-visible {
23
37
  position: absolute;
24
38
  bottom: 100%;
25
39
  left: 0;
26
- margin-bottom: 0.35rem;
27
- padding: 0.25rem 0.5rem;
28
- border-radius: 4px;
29
- background: #111827;
30
- color: #fff;
31
- font-size: 0.85rem;
40
+ margin-bottom: 0.4rem;
41
+ padding: 0.375rem 0.625rem;
42
+ border-radius: var(--semmet-radius-sm);
43
+ background: var(--semmet-color-inverse-surface);
44
+ color: var(--semmet-color-inverse-on-surface);
45
+ font-size: 0.8125rem;
46
+ line-height: 1.4;
32
47
  white-space: nowrap;
48
+ box-shadow: var(--semmet-elevation-1);
33
49
  }