@adarsh.nishad/activityual-ui 0.0.0-stage → 0.2.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,3 +1,211 @@
1
- # Temporary Holding Version
1
+ # @adarsh.nishad/activityual-ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A modern, accessible, high-performance Web Component library for the **Activityual** personal habit rhythm and quantified-self application, built with [Lit](https://lit.dev/).
4
+
5
+ ---
6
+
7
+ ## 🎨 Design System
8
+
9
+ `activityual-ui` is crafted based on the **Vaulto Obsidian** design system:
10
+ - **Primary Color:** Vaulto Signature Orange (`#FF4305` / `#E93B00`) with ambient neon glow
11
+ - **Secondary / Success Color:** Financial Positive Emerald (`#25D366` / `rgba(37, 211, 102, 0.12)`)
12
+ - **Negative / Missed Color:** Crimson Red (`#FF3434` / `rgba(255, 52, 52, 0.12)`)
13
+ - **Pending / Neutral Color:** Amber (`#F59E0B`) & Slate (`#A5A5A5`)
14
+ - **Backgrounds & Surfaces:** Tiered charcoal foundation (`#080808`, `#1B1B1B`, `#202020`, `#272727`)
15
+ - **Glassmorphism & Depth:** Translucent cards with 20px–28px backdrop blur and low-chrome ambient borders
16
+ - **Typography:** Sans-serif interface (`Inter`, `'SF Pro Display'`) & telemetry numbers (`'JetBrains Mono'`)
17
+
18
+ ---
19
+
20
+ ## 📦 Components
21
+
22
+ | Component | Description | Docs |
23
+ | :--- | :--- | :--- |
24
+ | **`activityual-button`** | Interactive pill button (`primary`, `secondary`, `amber`, `charcoal`, `ghost`, `danger`) with loading spinner | [Docs](docs/button.md) |
25
+ | **`activityual-card`** | Obsidian glass container card (`default`, `amber`, `inset`, `shell`) with customizable padding | [Docs](docs/card.md) |
26
+ | **`activityual-activity-card`** | Core habit ritual card with category glyph, streak badge, 5-day consistency trail, and interactive action buttons | [Docs](docs/activity-card.md) |
27
+ | **`activityual-stat-card`** | Metrics and KPI display card with delta pill indicators and monospace values | [Docs](docs/stat-card.md) |
28
+ | **`activityual-progress-ring`** | Circular SVG progress ring with animated stroke and automatic emerald completion state | [Docs](docs/progress-ring.md) |
29
+ | **`activityual-streak-badge`** | Visual flame streak badge displaying active day counts with optional glow highlight | [Docs](docs/streak-badge.md) |
30
+ | **`activityual-status-pill`** | Activity occurrence status indicators (`done`, `missed`, `pending`) in `sm` and `md` sizes | [Docs](docs/status-pill.md) |
31
+ | **`activityual-chart-panel`** | Weekly consistency and rhythm bar chart panel mapped against 80% target baseline | [Docs](docs/chart-panel.md) |
32
+ | **`activityual-recommendation-banner`** | AI Coach habit optimization banner displaying suggested execution windows & reasons | [Docs](docs/recommendation-banner.md) |
33
+ | **`activityual-empty-state`** | Dashed-border presentation placeholder for empty habit lists and categories | [Docs](docs/empty-state.md) |
34
+ | **`activityual-confirm-dialog`** | Accessible confirmation modal dialog with 28px backdrop blur and danger mode | [Docs](docs/confirm-dialog.md) |
35
+ | **`activityual-form-field`** | Styled form input wrapper with floating label, validation error feedback, and required star | [Docs](docs/form-field.md) |
36
+
37
+ ---
38
+
39
+ ## 🚀 Getting Started
40
+
41
+ ### Installation
42
+
43
+ ```bash
44
+ npm install @adarsh.nishad/activityual-ui
45
+ ```
46
+
47
+ ### Start Dev Server (with Playground Demo)
48
+
49
+ Run the local interactive design system playground:
50
+
51
+ ```bash
52
+ # Directly from packages/activityual-ui:
53
+ npm start
54
+
55
+ # Or from repository root:
56
+ npm run start:ui
57
+ ```
58
+
59
+ ### Build for Production
60
+
61
+ Compile all TypeScript elements and type definitions into `dist/`:
62
+
63
+ ```bash
64
+ # Directly in package:
65
+ npm run build
66
+
67
+ # Or from repository root:
68
+ npm run build:ui
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 💻 Usage
74
+
75
+ ### 1. In HTML / Vanilla JavaScript
76
+
77
+ ```html
78
+ <script type="module" src="https://unpkg.com/@adarsh.nishad/activityual-ui"></script>
79
+
80
+ <!-- Activity Card with Streak Badge & Status -->
81
+ <activityual-activity-card
82
+ activity-id="act-1"
83
+ title="Morning Sunlight & Hydration"
84
+ category="Health"
85
+ frequency="Daily • 07:30 AM"
86
+ streak="14"
87
+ status="done"
88
+ notes="Drink 500ml water and get 15 min direct sunlight"
89
+ ></activityual-activity-card>
90
+
91
+ <!-- Circular Progress Ring -->
92
+ <activityual-progress-ring percentage="85" label="Weekly Flow" size="130"></activityual-progress-ring>
93
+ ```
94
+
95
+ ### 2. In SvelteKit / Web Applications (`apps/web`)
96
+
97
+ In `src/routes/+layout.svelte` or your application entry point:
98
+
99
+ ```typescript
100
+ import '@adarsh.nishad/activityual-ui';
101
+ ```
102
+
103
+ Then use the custom elements directly in any `.svelte` file:
104
+
105
+ ```svelte
106
+ <activityual-stat-card
107
+ title="Completion Rhythm"
108
+ value="88.4%"
109
+ subtitle="vs 82.0% baseline"
110
+ delta="+6.4%"
111
+ delta-type="positive"
112
+ ></activityual-stat-card>
113
+
114
+ <activityual-button
115
+ variant="primary"
116
+ size="md"
117
+ onclick={() => console.log('Action triggered')}
118
+ >
119
+ Record Completion
120
+ </activityual-button>
121
+ ```
122
+
123
+ ### 3. Individual Subpath Imports (Tree-Shaking)
124
+
125
+ Import individual custom elements without loading the entire bundle:
126
+
127
+ ```typescript
128
+ import '@adarsh.nishad/activityual-ui/button';
129
+ import '@adarsh.nishad/activityual-ui/activity-card';
130
+ import '@adarsh.nishad/activityual-ui/progress-ring';
131
+ import '@adarsh.nishad/activityual-ui/stat-card';
132
+ ```
133
+
134
+ ### 4. Design Tokens Direct Access
135
+
136
+ Import the typed Vaulto Obsidian token dictionary directly in JavaScript / TypeScript:
137
+
138
+ ```typescript
139
+ import { tokens } from '@adarsh.nishad/activityual-ui';
140
+
141
+ console.log(tokens.colors.primary); // #FF4305
142
+ console.log(tokens.colors.canvasBase); // #080808
143
+ ```
144
+
145
+ ---
146
+
147
+ ## 📦 Building & Publishing to npm
148
+
149
+ Follow these steps whenever you make changes to `packages/activityual-ui` and want to publish a new release:
150
+
151
+ ### Step 1: Verify npm Authentication
152
+
153
+ ```bash
154
+ npm whoami
155
+ ```
156
+
157
+ If not logged in, run:
158
+
159
+ ```bash
160
+ npm login
161
+ ```
162
+
163
+ ### Step 2: Build the Library
164
+
165
+ Compile all custom elements and TypeScript type definitions into `dist/`:
166
+
167
+ ```bash
168
+ # From repository root:
169
+ npm run build:ui
170
+
171
+ # Or directly in this directory:
172
+ npm run build
173
+ ```
174
+
175
+ ### Step 3: Bump Version
176
+
177
+ Bump the semantic version (`patch`, `minor`, or `major`):
178
+
179
+ ```bash
180
+ # From repository root:
181
+ npm run version:ui:patch # e.g. 0.1.0 -> 0.1.1
182
+ # or
183
+ npm run version:ui:minor # e.g. 0.1.0 -> 0.2.0
184
+ # or
185
+ npm run version:ui:major # e.g. 0.1.0 -> 1.0.0
186
+
187
+ # Or navigate directly:
188
+ cd packages/activityual-ui
189
+ npm version patch # or minor / major
190
+ ```
191
+
192
+ ### Step 4: Publish to npm
193
+
194
+ Since it is a scoped package (`@adarsh.nishad/...`), publish with public access:
195
+
196
+ ```bash
197
+ # From repository root:
198
+ npm run publish:ui
199
+
200
+ # Or navigate directly:
201
+ cd packages/activityual-ui
202
+ npm publish --access public
203
+ ```
204
+
205
+ ---
206
+
207
+ ## 🛠️ Development
208
+
209
+ - **Tag Convention:** All custom elements must use the `activityual-` tag prefix (e.g. `activityual-button`, `activityual-activity-card`).
210
+ - **Design Tokens:** Keep design tokens synced with `src/tokens.ts` and `src/tokens.json`.
211
+ - **Type Checking:** Run `npm run check` before committing changes.
@@ -0,0 +1,43 @@
1
+ import { LitElement } from 'lit';
2
+ import './status-pill.js';
3
+ import './button.js';
4
+ export declare class ActivityualActivityCard extends LitElement {
5
+ static properties: {
6
+ activityId: {
7
+ type: StringConstructor;
8
+ };
9
+ title: {
10
+ type: StringConstructor;
11
+ };
12
+ category: {
13
+ type: StringConstructor;
14
+ };
15
+ frequency: {
16
+ type: StringConstructor;
17
+ };
18
+ status: {
19
+ type: StringConstructor;
20
+ };
21
+ streak: {
22
+ type: NumberConstructor;
23
+ };
24
+ notes: {
25
+ type: StringConstructor;
26
+ };
27
+ readOnly: {
28
+ type: BooleanConstructor;
29
+ };
30
+ };
31
+ activityId: string;
32
+ title: string;
33
+ category: string;
34
+ frequency: string;
35
+ status: 'done' | 'missed' | 'pending';
36
+ streak: number;
37
+ notes: string;
38
+ readOnly: boolean;
39
+ static styles: import("lit").CSSResult;
40
+ getCategoryIcon(cat: string): string;
41
+ handleAction(status: 'done' | 'missed'): void;
42
+ render(): import("lit-html").TemplateResult<1>;
43
+ }
@@ -0,0 +1,302 @@
1
+ import { LitElement, html, css } from 'lit';
2
+ import './status-pill.js';
3
+ import './button.js';
4
+ export class ActivityualActivityCard extends LitElement {
5
+ constructor() {
6
+ super(...arguments);
7
+ this.activityId = '';
8
+ this.title = '';
9
+ this.category = 'General';
10
+ this.frequency = 'Daily';
11
+ this.status = 'pending';
12
+ this.streak = 0;
13
+ this.notes = '';
14
+ this.readOnly = false;
15
+ }
16
+ static { this.properties = {
17
+ activityId: { type: String },
18
+ title: { type: String },
19
+ category: { type: String },
20
+ frequency: { type: String },
21
+ status: { type: String },
22
+ streak: { type: Number },
23
+ notes: { type: String },
24
+ readOnly: { type: Boolean }
25
+ }; }
26
+ static { this.styles = css `
27
+ :host {
28
+ display: block;
29
+ box-sizing: border-box;
30
+ }
31
+
32
+ .card {
33
+ background: linear-gradient(145deg, rgba(42, 42, 42, 0.65) 0%, rgba(24, 24, 24, 0.8) 100%);
34
+ backdrop-filter: blur(20px);
35
+ -webkit-backdrop-filter: blur(20px);
36
+ border: 1px solid rgba(255, 255, 255, 0.05);
37
+ border-radius: 14px;
38
+ padding: 1rem 1.25rem;
39
+ box-shadow:
40
+ 0 8px 24px -4px rgba(0, 0, 0, 0.45),
41
+ inset 0 1px 0 rgba(255, 255, 255, 0.08);
42
+ font-family: Inter, 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: space-between;
46
+ gap: 1rem;
47
+ transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1);
48
+ box-sizing: border-box;
49
+ }
50
+
51
+ .card:hover {
52
+ background: linear-gradient(145deg, rgba(48, 48, 48, 0.75) 0%, rgba(28, 28, 28, 0.88) 100%);
53
+ transform: translateY(-1px);
54
+ border-color: rgba(255, 255, 255, 0.1);
55
+ box-shadow:
56
+ 0 14px 32px -6px rgba(0, 0, 0, 0.55),
57
+ inset 0 1px 0 rgba(255, 255, 255, 0.12);
58
+ }
59
+
60
+ .left-cluster {
61
+ display: flex;
62
+ align-items: center;
63
+ gap: 0.85rem;
64
+ min-width: 0;
65
+ flex: 1;
66
+ }
67
+
68
+ .category-icon {
69
+ width: 38px;
70
+ height: 38px;
71
+ border-radius: 10px;
72
+ background: rgba(255, 67, 5, 0.1);
73
+ border: 1px solid rgba(255, 67, 5, 0.2);
74
+ color: #ff4305;
75
+ display: flex;
76
+ align-items: center;
77
+ justify-content: center;
78
+ font-size: 16px;
79
+ flex-shrink: 0;
80
+ box-shadow: 0 0 10px rgba(255, 67, 5, 0.15);
81
+ }
82
+
83
+ .info {
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: 0.2rem;
87
+ min-width: 0;
88
+ }
89
+
90
+ .title-row {
91
+ display: flex;
92
+ align-items: center;
93
+ gap: 0.5rem;
94
+ flex-wrap: wrap;
95
+ }
96
+
97
+ .title {
98
+ font-size: 13.5px;
99
+ font-weight: 600;
100
+ color: #f5f5f5;
101
+ margin: 0;
102
+ white-space: nowrap;
103
+ overflow: hidden;
104
+ text-overflow: ellipsis;
105
+ letter-spacing: -0.01em;
106
+ }
107
+
108
+ .meta {
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 0.55rem;
112
+ font-size: 11px;
113
+ color: #a5a5a5;
114
+ font-family: 'JetBrains Mono', monospace;
115
+ }
116
+
117
+ .meta-tag {
118
+ display: inline-flex;
119
+ align-items: center;
120
+ gap: 0.25rem;
121
+ }
122
+
123
+ .streak-tag {
124
+ color: #ff4305;
125
+ font-weight: 600;
126
+ }
127
+
128
+ .notes {
129
+ font-size: 11.5px;
130
+ color: #707070;
131
+ margin: 0.2rem 0 0 0;
132
+ display: -webkit-box;
133
+ -webkit-line-clamp: 1;
134
+ -webkit-box-orient: vertical;
135
+ overflow: hidden;
136
+ }
137
+
138
+ .dot-trail {
139
+ display: flex;
140
+ align-items: center;
141
+ gap: 4px;
142
+ margin-top: 4px;
143
+ }
144
+
145
+ .dot {
146
+ width: 6px;
147
+ height: 6px;
148
+ border-radius: 50%;
149
+ background: rgba(255, 255, 255, 0.12);
150
+ }
151
+
152
+ .dot.done {
153
+ background: #25d366;
154
+ box-shadow: 0 0 6px rgba(37, 211, 102, 0.4);
155
+ }
156
+
157
+ .dot.missed {
158
+ background: #ff3434;
159
+ box-shadow: 0 0 6px rgba(255, 52, 52, 0.4);
160
+ }
161
+
162
+ .right-cluster {
163
+ display: flex;
164
+ align-items: center;
165
+ gap: 0.65rem;
166
+ flex-shrink: 0;
167
+ }
168
+
169
+ .action-btn {
170
+ width: 32px;
171
+ height: 32px;
172
+ border-radius: 9999px;
173
+ border: 1px solid rgba(255, 255, 255, 0.08);
174
+ background: rgba(255, 255, 255, 0.04);
175
+ display: flex;
176
+ align-items: center;
177
+ justify-content: center;
178
+ cursor: pointer;
179
+ font-size: 13px;
180
+ font-weight: 700;
181
+ color: #a5a5a5;
182
+ transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
183
+ outline: none;
184
+ }
185
+
186
+ .action-btn:focus-visible {
187
+ box-shadow: 0 0 0 2px rgba(255, 67, 5, 0.4);
188
+ }
189
+
190
+ .action-done:hover {
191
+ background: rgba(37, 211, 102, 0.2);
192
+ border-color: #25d366;
193
+ color: #25d366;
194
+ box-shadow: 0 0 10px rgba(37, 211, 102, 0.3);
195
+ transform: scale(1.05);
196
+ }
197
+
198
+ .action-done.active {
199
+ background: #25d366;
200
+ border-color: #25d366;
201
+ color: #ffffff;
202
+ box-shadow: 0 0 12px rgba(37, 211, 102, 0.4);
203
+ }
204
+
205
+ .action-missed:hover {
206
+ background: rgba(255, 52, 52, 0.2);
207
+ border-color: #ff3434;
208
+ color: #ff3434;
209
+ box-shadow: 0 0 10px rgba(255, 52, 52, 0.3);
210
+ transform: scale(1.05);
211
+ }
212
+
213
+ .action-missed.active {
214
+ background: #ff3434;
215
+ border-color: #ff3434;
216
+ color: #ffffff;
217
+ box-shadow: 0 0 12px rgba(255, 52, 52, 0.4);
218
+ }
219
+ `; }
220
+ getCategoryIcon(cat) {
221
+ const lower = cat.toLowerCase();
222
+ if (lower.includes('health') || lower.includes('hydration') || lower.includes('water'))
223
+ return '💧';
224
+ if (lower.includes('fit') || lower.includes('workout') || lower.includes('sport'))
225
+ return '⚡';
226
+ if (lower.includes('mind') || lower.includes('read') || lower.includes('meditat'))
227
+ return '📖';
228
+ if (lower.includes('code') || lower.includes('deep') || lower.includes('work'))
229
+ return '🎯';
230
+ return '✨';
231
+ }
232
+ handleAction(status) {
233
+ this.dispatchEvent(new CustomEvent('status-change', {
234
+ detail: { activityId: this.activityId, status },
235
+ bubbles: true,
236
+ composed: true
237
+ }));
238
+ }
239
+ render() {
240
+ return html `
241
+ <div class="card">
242
+ <div class="left-cluster">
243
+ <div class="category-icon" aria-hidden="true">
244
+ ${this.getCategoryIcon(this.category)}
245
+ </div>
246
+ <div class="info">
247
+ <div class="title-row">
248
+ <h3 class="title">${this.title}</h3>
249
+ </div>
250
+ <div class="meta">
251
+ <span class="meta-tag">${this.category}</span>
252
+ <span>•</span>
253
+ <span class="meta-tag">${this.frequency}</span>
254
+ ${this.streak > 0
255
+ ? html `
256
+ <span>•</span>
257
+ <span class="meta-tag streak-tag">🔥 ${this.streak}d streak</span>
258
+ `
259
+ : ''}
260
+ </div>
261
+ ${this.notes ? html `<p class="notes">${this.notes}</p>` : ''}
262
+ <div class="dot-trail" aria-label="Recent consistency">
263
+ <span class="dot done"></span>
264
+ <span class="dot done"></span>
265
+ <span class="dot done"></span>
266
+ <span class="dot done"></span>
267
+ <span class="dot ${this.status === 'done' ? 'done' : this.status === 'missed' ? 'missed' : ''}"></span>
268
+ </div>
269
+ </div>
270
+ </div>
271
+
272
+ <div class="right-cluster">
273
+ <activityual-status-pill .status="${this.status}" size="sm"></activityual-status-pill>
274
+
275
+ ${!this.readOnly
276
+ ? html `
277
+ <button
278
+ class="action-btn action-done ${this.status === 'done' ? 'active' : ''}"
279
+ title="Mark Done"
280
+ aria-label="Mark ${this.title} Done"
281
+ @click="${() => this.handleAction('done')}"
282
+ >
283
+ ✓
284
+ </button>
285
+ <button
286
+ class="action-btn action-missed ${this.status === 'missed' ? 'active' : ''}"
287
+ title="Mark Missed"
288
+ aria-label="Mark ${this.title} Missed"
289
+ @click="${() => this.handleAction('missed')}"
290
+ >
291
+ ✕
292
+ </button>
293
+ `
294
+ : ''}
295
+ </div>
296
+ </div>
297
+ `;
298
+ }
299
+ }
300
+ if (!customElements.get('activityual-activity-card')) {
301
+ customElements.define('activityual-activity-card', ActivityualActivityCard);
302
+ }
@@ -0,0 +1,32 @@
1
+ import { LitElement } from 'lit';
2
+ export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'charcoal' | 'amber';
3
+ export type ButtonSize = 'sm' | 'md' | 'lg';
4
+ export declare class ActivityualButton extends LitElement {
5
+ static properties: {
6
+ variant: {
7
+ type: StringConstructor;
8
+ };
9
+ size: {
10
+ type: StringConstructor;
11
+ };
12
+ disabled: {
13
+ type: BooleanConstructor;
14
+ reflect: boolean;
15
+ };
16
+ loading: {
17
+ type: BooleanConstructor;
18
+ reflect: boolean;
19
+ };
20
+ type: {
21
+ type: StringConstructor;
22
+ };
23
+ };
24
+ variant: ButtonVariant;
25
+ size: ButtonSize;
26
+ disabled: boolean;
27
+ loading: boolean;
28
+ type: string;
29
+ static styles: import("lit").CSSResult;
30
+ handleClick(e: MouseEvent): void;
31
+ render(): import("lit-html").TemplateResult<1>;
32
+ }