@soulparking/design-system
v0.1.1
Published
SPN Design System — Vue 3 Web Components
Readme
@soulparking/design-system
SoulParking Design System — Vue 3 components published as framework-agnostic Web Components.
Full documentation & live component playground: spn-ds.balbalcode.my.id
Installation
npm install @soulparking/design-systemUsage
Option A — Web Components (React, Angular, Vanilla JS, any framework)
Register all custom elements once in your app entry point:
import '@soulparking/design-system/dist/es/web-components/index.js'
import '@soulparking/design-system/dist/style.css'Then use the HTML tags anywhere:
<spn-button variant="primary" text="Click me"></spn-button>
<spn-input placeholder="Enter email"></spn-input>
<spn-badge variant="success" label="Active"></spn-badge>Option B — Vue 3 Components (tree-shakeable)
import { ActionButton, InputTextGroup, DataTable } from '@soulparking/design-system'
import '@soulparking/design-system/dist/style.css'Web Component Tags
| Tag | Description |
|-----|-------------|
| <spn-button> | Action button |
| <spn-input> | Text input |
| <spn-input-group> | Labeled input with hint + error |
| <spn-textarea> | Textarea |
| <spn-textarea-group> | Labeled textarea |
| <spn-select> | Select dropdown |
| <spn-select-group> | Labeled select |
| <spn-checkbox> | Checkbox |
| <spn-checkbox-group> | Labeled checkbox group |
| <spn-radio> | Radio button |
| <spn-radio-group> | Labeled radio group |
| <spn-switch> | Toggle switch |
| <spn-badge> | Status badge |
| <spn-tag> | Tag / chip |
| <spn-card-input> | Card-style selectable input |
| <spn-file-input> | File upload with drag-and-drop + S3 support |
| <spn-file-input-group> | Labeled file input |
| <spn-image-input-group> | Image upload with preview |
| <spn-pagination> | Pagination controls |
Component Reference
<spn-button>
<spn-button
variant="primary"
text="Save"
size="md"
is-disabled="true"
></spn-button>| Prop | Values | Default |
|------|--------|---------|
| variant | primary secondary outline ghost danger | primary |
| size | sm md lg | md |
| text | string | — |
| is-disabled | "true" | — |
<spn-input>
<spn-input
placeholder="[email protected]"
is-password="true"
></spn-input>
<!-- With prefix icon (slot) -->
<spn-input placeholder="Email">
<svg slot="prefix" ...></svg>
</spn-input>| Prop | Description |
|------|-------------|
| placeholder | Input placeholder |
| is-password | "true" — shows password toggle |
| disabled | "true" |
Event: update:modelValue — fired on every keystroke with event.detail = current value.
<spn-input-group>
<spn-input-group
label="Email address"
hint="We'll never share your email."
error="Required field"
label-action="Forgot?"
></spn-input-group>| Prop | Description |
|------|-------------|
| label | Field label |
| hint | Helper text below the input |
| error | Error message (red state) |
| label-action | Clickable link text next to the label |
Event: label-action-click — fired when the label action link is clicked.
<spn-file-input>
Drag-and-drop file upload widget with per-file progress, done, and error states. Supports S3 presigned URL upload.
<spn-file-input
accept=".pdf,.docx"
hint="PDF or DOCX up to 10MB"
multiple="true"
max-size="10"
upload-endpoint="https://api.example.com/upload/presign"
auth-token="Bearer eyJ..."
></spn-file-input>| Prop | Description |
|------|-------------|
| accept | MIME types or extensions (e.g. ".pdf,image/*") |
| hint | Helper text inside the drop zone |
| multiple | "true" — allow multiple files |
| max-size | Max file size in MB |
| upload-endpoint | URL to fetch a presigned upload URL |
| auth-token | Bearer token sent in Authorization header |
The endpoint must return:
{ "url": "https://s3.amazonaws.com/...", "fields": { ... } }Events: update:modelValue (array of uploaded URLs), upload-error, files
<spn-image-input-group>
Image upload with 16:9 preview.
<spn-image-input-group
label="Cover photo"
accept="image/*"
upload-endpoint="https://api.example.com/upload/presign"
auth-token="Bearer eyJ..."
preview-base-url="https://cdn.example.com"
></spn-image-input-group><spn-badge>
<spn-badge variant="success" label="Active"></spn-badge>
<spn-badge variant="warning" label="Pending"></spn-badge>
<spn-badge variant="danger" label="Inactive"></spn-badge>| variant | |
|-----------|--|
| default success warning danger info | |
<spn-pagination>
<spn-pagination
current-page="1"
total-pages="10"
per-page="10"
total-items="98"
></spn-pagination>Event: page-change — event.detail = new page number.
Framework Integration
React
Web Components emit CustomEvent, not React synthetic events. Wrap them with a small bridge:
import { useRef, useEffect, useCallback } from 'react'
function SpnInput({ value, onChange, ...props }) {
const ref = useRef(null)
useEffect(() => {
if (ref.current) ref.current.modelValue = value ?? ''
}, [value])
const stable = useCallback(onChange, [])
useEffect(() => {
const el = ref.current
if (!el) return
const handler = (e) => stable(e.detail)
el.addEventListener('update:modelValue', handler)
return () => el.removeEventListener('update:modelValue', handler)
}, [stable])
return <spn-input ref={ref} {...props} />
}Angular
Add CUSTOM_ELEMENTS_SCHEMA to your module or standalone component:
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
@Component({
standalone: true,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
template: `<spn-input #inputRef placeholder="Email"></spn-input>`,
})
export class MyComponent implements AfterViewInit {
@ViewChild('inputRef') inputRef!: ElementRef
ngAfterViewInit() {
this.inputRef.nativeElement
.addEventListener('update:modelValue', (e: CustomEvent) => {
this.value = e.detail
})
}
}Vue 3
Use Vue components directly for full reactivity:
<script setup>
import { ref } from 'vue'
import { InputTextGroup, ActionButton } from '@soulparking/design-system'
import '@soulparking/design-system/dist/style.css'
const email = ref('')
</script>
<template>
<InputTextGroup label="Email" v-model="email" />
<ActionButton variant="primary" text="Submit" />
</template>Documentation
Full interactive documentation is available at spn-ds.balbalcode.my.id, including:
- Live component playground (Storybook)
- All props, slots, and events per component
- Design tokens and color palette
- Copy-paste code examples for Vue, React, and Angular
Publishing
Requires Node 20+.
nvm use 20
npm version patch # bump version
npm publish --otp 123456 # 6-digit code from authenticatorLicense
Private — SoulParking internal use.
