OVS - A pure JavaScript declarative UI, a Vue DSL that lets you write Vue with a more concise, elegant, and flexible syntax, inspired by Kotlin-HTML, SwiftUI, and SolidJS.
English | 中文
Quick Start • Basic Syntax • Advanced Usage • Design Philosophy • Compilation Principles
OVS is a declarative UI syntax extension that lets you write Vue components in a more concise way:
// OVS syntax
div(class = 'container') {
h1 { 'Hello OVS!' }
button(onClick() { handleClick() }) { 'Click Me' }
}Features:
- ✅ Pure JavaScript Superset - All JS syntax works
- ✅ Zero Template Directives - No
v-if,v-for, just nativeif/for - ✅ No JSX Required - Native brace syntax, no
className, no{}wrapping - ✅ Full IDE Support - Code completion, type checking, go to definition
- ✅ Vue 3 Runtime - Compiles to efficient Vue render functions
OVS solves the flexibility problem of Vue templates while being more elegant than JSX.
Conditional Rendering:
// ❌ Vue Template - Custom directives, limited flexibility
<template>
<div v-if="isLoggedIn">Welcome, {{ username }}</div>
<div v-else>Please login</div>
</template>
// ❌ JSX - Ternary expressions, hard to read when nested
{isLoggedIn ? <div>Welcome, {username}</div> : <div>Please login</div>}
// ✅ OVS - Native JavaScript, clean and intuitive
if (isLoggedIn) {
div { `Welcome, ${username}` }
} else {
div { 'Please login' }
}List Rendering:
// ❌ Vue Template - v-for directive, special :key syntax
<template>
<ul>
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
</ul>
</template>
// ❌ JSX - .map() callback, requires return and key prop
<ul>
{items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
// ✅ OVS - Native for...of, straightforward
ul {
for (const item of items) {
li { item.name }
}
}Complex Logic:
// ❌ Vue Template - Need computed properties or methods for complex logic
// ❌ JSX - Inline logic becomes messy with && and ternary chains
// ✅ OVS - Just write JavaScript!
div {
const total = items.reduce((sum, item) => sum + item.price, 0)
if (total > 100) {
span(class = 'discount') { 'Free shipping!' }
}
for (const item of items) {
if (item.inStock) {
ProductCard(product = item) { }
}
}
}| Feature | Vue Template | JSX | OVS |
|---|---|---|---|
| Control Flow | v-if, v-for directives |
&&, ? :, .map() |
Native if, for |
| Class Attribute | class |
className |
class |
| Expression Syntax | {{ }} |
{ } |
Direct reference |
| Learning Curve | Template syntax | JSX rules | Just JavaScript |
| Flexibility | Limited | High | High |
| Readability | Good for simple | Complex nesting | Clean & intuitive |
npm create ovs@latest my-app
cd my-app
npm install
npm run devInstall Ovs Language from VSCode Extensions Marketplace.
Create a .ovs file:
// src/components/Hello.ovs
import { ref } from 'vue'
const count = ref(0)
div(class = 'hello') {
h1 { 'Hello OVS!' }
p { `Count: ${count.value}` }
button(onClick() { count.value++ }) { '+1' }
}Use tagName { content } to declare elements:
div { 'Hello World' }
// Nested elements
div {
h1 { 'Title' }
p { 'Content' }
}Use tagName(prop = value) { content } to pass props:
div(class = 'container', id = 'app') {
a(href = 'https://example.com', target = '_blank') {
'Click here'
}
}
// Event handling - method shorthand
button(onClick() { console.log('clicked') }) {
'Click Me'
}
// Shorthand property (like ES6 object shorthand)
const disabled = true
button(disabled) { 'Submit' }
// Spread props
const props = { class: 'btn', id: 'submit' }
button(...props) { 'Submit' }Write strings or JavaScript expressions directly:
div {
'Static text' // Static text
`Dynamic: ${value}` // Template string
someVariable // Variable
computedValue() // Function call
}Use standard JavaScript conditional statements:
div {
if (isLoggedIn) {
span { `Welcome, ${username}` }
} else {
button { 'Login' }
}
}Use for...of loops:
ul {
for (const item of items) {
li { item.name }
}
}Use the view soft keyword to define reusable components:
// Define component
view Card(props) {
div(class = 'card') {
h2 { props.title }
p { props.content }
props.children // Render children
}
}
// Use component
Card(title = 'Hello', content = 'World') {
span { 'Extra content' }
}Note:
viewis a soft keyword (contextual keyword), only has special meaning at component declaration position. You can still useviewas a variable name elsewhere:const view = someValue
Code inside #{} won't be rendered to DOM, used for pure logic:
div {
#{
// Pure JS logic here, won't render
const data = processData(rawData)
console.log('Processing...')
}
// This will render
span { data.result }
}Works with Vue's ref and reactive:
import { ref, reactive } from 'vue'
const count = ref(0)
const user = reactive({ name: 'Alice', age: 25 })
div {
p { `Count: ${count.value}` }
p { `Name: ${user.name}` }
button(onClick() { count.value++ }) { 'Add' }
}// HelloWorld.ovs
import { ref } from 'vue'
// Define a sub-component
view CountDisplay(props) {
div(class = 'count-display') {
span { 'Current count: ' }
strong(style = 'color: #42b883; font-size: 24px;') { props.count }
}
}
// Main view
div(class = 'greetings', onClick() { count.value = 0 }) {
const msg = "You did it!"
let count = ref(0)
// Timer to increment count
const timer = setInterval(() => {
count.value = count.value + 1
}, 1000)
h1(class = 'green') { msg }
h3 {
"Built with "
a(href = 'https://vuejs.org/', target = '_blank') { 'Vue 3' }
' + '
a(href = 'https://github.com/alamhubb/ovsjs', target = '_blank') { 'OVS' }
}
// Inline element assignment
const countView = span { count }
// Use component with props
CountDisplay(count = countView) { }
p(style = 'color: #888; font-size: 12px;') { '(Click anywhere to reset)' }
}OVS only adds three syntax extensions:
tag {}/tag(props) {}- Element declaration with OvsArgumentsview Name(props) {}- Component declaration (viewis a soft keyword)#{}- Non-rendering block
Everything else is standard JavaScript, minimal learning curve.
OVS uses a special props syntax that's cleaner than object literals:
// OvsArguments syntax (OVS)
div(class = 'container', id = 'app', onClick() { handle() }) {
'content'
}
// Compiles to:
$OvsHtmlTag.div({ class: 'container', id: 'app', onClick() { handle() } }, ['content'])OvsArguments features:
prop = value- Property assignment (uses=instead of:)method() {}- Method shorthandshorthand- Shorthand property (like ES6)...spread- Spread operator
OVS compiler generates precise Source Maps, enabling IDE to:
- Accurately locate original
.ovsfile positions - Provide complete TypeScript type checking
- Support go-to-definition, rename, and other refactoring features
| Component | Description |
|---|---|
| Subhuti | Parser generator framework, define grammar rules with decorators |
| Slime | JavaScript/TypeScript fault-tolerant parser |
| Volar | Language Server framework, provides IDE support |
| Vue 3 | Runtime framework |
test-volar/
├── ovs/ # OVS compiler + runtime
│ ├── ovs-compiler/ # Compiler (Parser + AST transform)
│ └── ovs-runtime/ # Runtime ($OvsHtmlTag + defineOvsComponent)
├── ovs-language/ # VSCode extension
├── create-ovs/ # Project scaffolding
├── vite-plugin-ovs/ # Vite plugin (integrates cssts)
├── cssts/ # CssTs core (css {} syntax compiler)
├── cssts-types/ # CssTs type definitions
├── cssts-theme-element/ # Element Plus theme atoms
├── cssts-ui/ # Element Plus fork (CssTs implementation)
├── vite-plugin-cssts/ # CssTs Vite plugin
├── slime/ # JS/TS parser
└── subhuti/ # Parser framework
cssts-ui/ is a fork of Element Plus, demonstrating how to rewrite a component library using CssTs atomic classes.
cssts-ui/
├── packages/
│ ├── components/ # Original Element Plus components (Vue SFC + SCSS)
│ ├── cssts-components/ # CssTs rewritten components (OVS + css {})
│ └── theme-chalk/ # Original SCSS theme
Original Element Plus (Vue SFC + SCSS):
- Components in
packages/components/ - Styles in
packages/theme-chalk/ - Uses BEM naming convention
- Requires SCSS compilation
CssTs Version (OVS + css {}):
- Components in
packages/cssts-components/ - Styles defined inline with
css {}syntax - Type-safe atomic classes
- No external CSS files needed
OVS integrates CssTs for type-safe atomic CSS styling:
// Define styles with css {} syntax
const buttonStyle = css {
displayInlineFlex,
justifyContentCenter,
alignItemsCenter,
height32px,
paddingLeft15px,
paddingRight15px,
fontSize14px,
borderRadius4px,
cursorPointer
}
// Use in OVS
button(class = buttonStyle) { 'Click Me' }Use $$ (double dollar sign) to declare pseudo-class styles:
// Variable name format: {baseName}$${pseudo1}$${pseudo2}...
const btn$$hover$$active = css { cursorPointer, displayFlex }
// Generated CSS:
// .cursor_pointer { cursor: pointer; }
// .btn:hover { opacity: 0.9; } ← from pseudoUtils config
// .btn:active { opacity: 0.6; } ← from pseudoUtils configCssTs uses property_value format for CSS class names:
| TS Variable | CSS Class | CSS Rule |
|---|---|---|
displayFlex |
.display_flex |
display: flex |
height32px |
.height_32px |
height: 32px |
justifyContentCenter |
.justify-content_center |
justify-content: center |
Important: Numbers must include units!
// ✅ Correct
height32px, fontSize14px, paddingLeft15px
// ❌ Wrong (missing units)
height32, fontSize14, paddingLeft15vite-plugin-ovs integrates vite-plugin-cssts internally:
// vite.config.ts
import ovs from 'vite-plugin-ovs'
export default defineConfig({
plugins: [
ovs({
cssts: {
pseudoUtils: {
hover: { opacity: '0.9' },
active: { opacity: '0.6' }
}
}
})
]
})How it works:
vite-plugin-ovscreates a sharedSet<string>for style collection- Both
.csstsand.ovsfiles write collected atoms to this shared Set virtual:cssts.cssgenerates CSS from all collected stylesvirtual:csstsAtomgenerates atom object mappings
.ovs file ──► ovs-compiler ──► usedAtoms ──┐
├──► sharedStyles ──► CSS
.cssts file ─► cssts-compiler ─► usedAtoms ┘
For detailed documentation, see:
- cssts/ARCHITECTURE.md - Architecture design
- cssts/cssts/README.md - Core compiler
- vite-plugin-cssts - Vite plugin
MIT License
Issues and Pull Requests are welcome!
- Documentation: deepwiki.com/alamhubb/ovsjs
- Topics:
vue-kotlinvue-dsldeclarative-vuedeclarative-ui-syntax-in-vuevue-swiftuivue-fluttervue-solidjsvue-dsl-in-pure-javascriptdeclarative-dsl-vue
OVS - Declarative UI for the Web, Concise & Elegant ✨