DEV Community

Cover image for Turning CSS Into a Smart UI Engine (Without JavaScript)
FSCSS tutorial for FSCSS tutorial

Posted on

Turning CSS Into a Smart UI Engine (Without JavaScript)

Modern UI development keeps pushing toward more dynamic, responsive, and intelligent interfaces. But what if some of that “logic” didn’t need JavaScript at all?

Today, I experimented with something interesting: building a fully dynamic circular progress system where values, colors, and styles are generated and computed directly inside CSS.

No loops in JS. No conditionals in JS.
Everything happens in styling.


⚡ The Goal

Create a circular progress component that:

  • Supports values from 0 → 100
  • Automatically generates classes like .p-0, .p-1, .p-2, to p-100
  • Dynamically changes color based on the value
  • Feels like a real UI component system

🧩 Setup

FSCSS CDN or it extention + npm

<script src="https://cdn.jsdelivr.net/npm/fscss@1.1.20/exec.min.js" async></script>
Enter fullscreen mode Exit fullscreen mode
@import(exec(_init circle-progress))
@progress-root()
@circle-progress(.progress)
Enter fullscreen mode Exit fullscreen mode

This initializes a reusable circular progress component.


🧠 Adding Logic to Styling

Instead of hardcoding colors, we define a reusable event:

@event range-color(range){

if range<=10{
    return: #ff6b6b;
}

el-if range<=30{
    return: #f7b267;
}

el-if range<=60{
    return: #74c0fc;
}

el-if range<=99{
    return: #63e6be;
}

el{
    return: #38d9a9;
}

}
Enter fullscreen mode Exit fullscreen mode

This acts like a function:

«Input → progress value
Output → corresponding UI color»


🔁 Generating 100 Classes Automatically

Instead of writing ".p-1", ".p-2", ".p-3" manually:

@arr p-ranges[count(100)]
Enter fullscreen mode Exit fullscreen mode

Now we loop through them:

.p-@arr.p-ranges[]{
    $range: @arr.p-ranges[];

    @progress-range($range)

    --progress-color-glow: @event.range-color($range);
    --progress-color-arc: @event.range-color($range);
}
Enter fullscreen mode Exit fullscreen mode

This generates:

.p-1 { ... }
.p-2 { ... }
...
.p-100 { ... }

Automatically.


🎨 Styling

body{
  background: #131213;
}
Enter fullscreen mode Exit fullscreen mode

🧪 Usage

<div class="progress p-10">10</div>
<div class="progress p-30">30</div>
<div class="progress p-55">55</div>
<div class="progress p-89">89</div>
<div class="progress p-100">100</div>
Enter fullscreen mode Exit fullscreen mode

✨ What’s Happening Here?

Each element:

  1. Gets its value from the class ("p-55")
  2. That value is passed into "@progress-range"
  3. The same value is sent into "@event range-color"
  4. The UI updates automatically

So the system becomes:

Class → Value → Logic → Style → UI


💡 Why This Is Interesting

This fscss approach introduces a different way to think about styling:

  1. CSS with Logic

You’re not just styling — you’re deciding behavior

  1. No Repetition

No need to manually define 100 variations

  1. Reusable Patterns

The same "@event" can power multiple components

  1. Component Thinking

Styles start behaving like UI modules


Example

<script src="https://cdn.jsdelivr.net/npm/fscss@1.1.20/exec.min.js"  async></script>

<style>

@import(exec(_init circle-progress))
@progress-root()
@circle-progress(.progress)

@event range-color(range){

if range<=10{
    return: #ff6b6b; /* soft red */
}

el-if range<=30{
    return: #f7b267; /* warm orange */
}

el-if range<=60{
    return: #74c0fc; /* light blue */
}

el-if range<=99{
    return: #63e6be; /* soft teal */
}

el{
    return: #38d9a9; /* fresh green */
}

}

body{
  background: #131213;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  padding: 20px;
  gap: 15px;
}
@arr p-ranges[count(100)]

.p-0 {
    @progress-range(0) 
    --progress-color-glow: @event.range-color(0);
    --progress-color-arc: @event.range-color(0); 
}

.p-@arr.p-ranges[]{
    $range: @arr.p-ranges[];
    @progress-range($range) 
    --progress-color-glow: @event.range-color($range);
    --progress-color-arc: @event.range-color($range); 
}


</style>

<div class="progress p-10">10</div>
<div class="progress p-30">30</div>
<div class="progress p-55">55</div>
<div class="progress p-89">89</div>
<div class="progress p-100">100</div>

Enter fullscreen mode Exit fullscreen mode

🔥 Real Use Cases

This pattern can be extended to:

  • Skill meters
  • File upload progress
  • Health/status indicators
  • Dashboard analytics
  • Gamification systems

We usually separate logic (JavaScript) and styling (CSS).
But this shows something interesting:

Styling itself can become expressive enough to handle structured logic.


Top comments (0)