DEV Community

Alexander Alemayhu
Alexander Alemayhu

Posted on

CSS in Imba - Inlined Syntax

In this post, I want to go into the language support Imba has for CSS. I am not talking about using <link href="..." rel="stylesheet">, but writing the CSS code within the same file of the element you are styling.

What is Imba?

Quickly before diving into the CSS, let me tell you a little bit about Imba 😊

Imba is a programming language for writing powerful web apps. It compiles down to JavaScript. The name comes from the gaming term imbalance. The interactive learning platform Scrimba is a heavy user of Imba. If you want to learn about the language, check out the official documentation at imba.io. It's all work in progress so feel free to report any issues you see or lack of things at the imba.io repository.

GitHub logo imba / imba.io

📄The official website of Imba

Official site for Imba. Work in progress.

Run

# clone the repository
git clone https://github.com/imba/imba.io

# enter the folder
cd imba.io

# install dependencies
npm install

# package content from content/ (continously)
npm run watch

# run server
npm start

Because the site uses service workers it requires https: with a trusted certificate. To test things in development you need to do it via Chrome launched with specific args:

open -na Google\ Chrome --args --ignore-certificate-errors --allow-insecure-localhost --unsafely-treat-insecure-origin-as-secure=https://localhost:9000
# if this does not work - try
# /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --allow-insecure-localhost --ignore-certificate-errors --unsafely-treat-insecure-origin-as-secure=https://localhost:9000
Enter fullscreen mode Exit fullscreen mode

Now visit https://localhost:9000/ in that browser.

Having trouble with https?

https is required to be able to run the examples and snippets. You can still run the rest of the site and work on documentation without this. So, if you're having trouble setting up https use the npm start-without-https command instead of the normal npm start

Inline Styles Properties on Tags

Before the 1.5.0 release, Imba already had syntax for writing CSS. Let's take a look at a line from the private Scrimba codebase (I have permission to use this ;)

    var embed = <div css:position="relative" css:paddingBottom="{9/16*100}%">

    # Compiled output below
    var _t1, Imba = require('imba'), _1 = Imba.createElementFactory(/*SCOPEID*/);
    var embed = (_1('div').css('position',"relative")).css('paddingBottom',("" + (9 / 16 * 100) + "%")).end();
Enter fullscreen mode Exit fullscreen mode

In the above snippet, we set the position and padding-bottom on the div element. The lines can get pretty long since it's inlined into the tag. One thing that some Imba programmers do is break it up into multiline:

    var embed = <div 
                css:position="relative" 
                css:paddingBottom="{9/16*100}%">
Enter fullscreen mode Exit fullscreen mode

But is that more readable? What if you want to set 7 - 23 different properties? This gets ugly fast. What is the solution to this? Enter SFC!

Single File Components

In Imba 1.5 experimental multi-line syntax for writing CSS landed. This is how it looks like:

    tag embed
        ### css
        .embed {
            position: relative;
            padding-bottom: 40px 
        }
        ###

    # Compiled output below 
    var styles = document.createElement('style');
    styles.textContent = ".embed{position:relative;padding-bottom:40px;}\n";
    document.head.appendChild(styles);
    var Imba = require('imba'), _1 = Imba.createTagScope("fcedc580");
    _1.defineTag('embed', function(tag){
        /* css
            .embed {
                position: relative;
                padding-bottom: 40px 
            }
            */

    });
Enter fullscreen mode Exit fullscreen mode

This enables us to create single file components and makes it so much easier to design interfaces one component at a time 😍

Optionally Scoped

Similar to Vue.js, the syntax also supports scoping the style to the specific component.

    tag embed
        ### css scoped
        .embed {
            position: relative;
            padding-bottom: 40px 
        }
        ###

    # Compiled output below
    var styles = document.createElement('style');
    styles.textContent = ".embed._fcedc580{position:relative;padding-bottom:40px;}\n";
    document.head.appendChild(styles);
    var Imba = require('imba'), _1 = Imba.createTagScope("fcedc580");
    _1.defineTag('embed', function(tag){
        /* css scoped
            .embed {
                position: relative;
                padding-bottom: 40px 
            }
            */

    });
Enter fullscreen mode Exit fullscreen mode

By using css scoped instead of css in the comment a class suffix is added. In the above example, ._fcedc580 is appended to the class name .embed.
What scoped means in simple terms is that only the elements of that tag type will be affected by the defined style.

Using scoped becomes very beneficial when you have lots of tags and files with various styles and would like to select specific things without affecting others. It might not make sense on a landing page but can pay off nicely when you have lots of different layouts and components.

Implementation - How is Imba doing it?

The syntax is heavily inspired by the way Vue.js does it in vue-loader and Vue SFC. Even the initial prototype used the @vue/component-compiler-utils package. While useful, unfortunately, that package has a lot of dependencies that would add too much overhead to the Imba language.

Fortunately, @sombee managed to replace the feature-set by using projects that could be added directly to the vendor directory. This keeps Imba at 0 dependencies 😊

GitHub logo mdevils / css-selector-parser

Just a CSS selector parser.

node-css-selector-parser Build Status

Fast and low memory CSS selector parser.

Parses CSS selector into object-model.

Installation

npm install css-selector-parser

Usage

var CssSelectorParser = require('css-selector-parser').CssSelectorParser,

    parser = new CssSelectorParser();

parser.registerSelectorPseudos('has');
parser.registerNestingOperators('>', '+', '~');
parser.registerAttrEqualityMods('^', '$', '*', '~');
parser.enableSubstitutes();

var util = require('util');

console.log(util.inspect(parser.parse('a[href^=/], .container:has(nav) > a[href]:lt($var)'), false, null));
Enter fullscreen mode Exit fullscreen mode

Produces:

{ type: 'selectors',
  selectors
   [ { type: 'ruleSet',
       rule:
        { tagName: 'a',
          attrs: [ { name: 'href', operator: '^=', valueType: 'string', value: '/' } ],
          type: 'rule'
Enter fullscreen mode Exit fullscreen mode

GitHub logo thysultan / stylis.js

light – weight css preprocessor

STYLIS

stylis

A Light–weight CSS Preprocessor.

Coverage Size Licence NPM

Installation

  • Use a Direct Download: <script src=stylis.js></script>
  • Use a CDN: <script src=unpkg.com/stylis></script>
  • Use NPM: npm install stylis --save

Features

  • nesting a { &:hover {} }
  • selector namespacing
  • vendor prefixing (flex-box, etc...)
  • minification
  • esm module compatible
  • tree-shaking-able

Abstract Syntax Structure

const declaration = {
    value: 'color:red;',
    type: 'decl',
    props: 'color',
    children: 'red',
    line: 1, column: 1
}
const comment = {
    value: '/*@noflip*/',
    type: 'comm',
    props: '/',
    children: '@noflip',
    line: 1, column: 1
}

const ruleset = {
    value: 'h1,h2',
    type: 'rule',
    props: ['h1', 'h2'],
    children: [/* ... */],
    line: 1, column: 1
}

const atruleset = {
    value: '@media (max-width:100), (min-width:100)',
    type: '@media',
Enter fullscreen mode Exit fullscreen mode

Both of these projects were picked due to their small footprint.

Imba Changelog

If you would like to learn about this interactively from a Cast, check out this screencast I did with @somebee on Scrimba

https://scrimba.com/p/pdq9quP/c7P26Ehg

Heads Up

The syntax is still considered experimental so its possible things will change but hopefully not too much. Some other temporary limitations in the compiler are

  • Not being able to handle values like ! and ' which are not encoded / decoded properly #243
  • Also /* [...] */ css comments cause compile errors #242

Those errors have been resolved and the related pull requests need to be merged as of this writing.

Summary

Writing CSS in Imba just got a whole lot easier and even maintaining it now is easier. If you would like to get started with Imba, I have created a boilerplate project based on the hello-world-imba template.

GitHub logo imba / hello-world-imba-sfc

Barebones Imba example using SFC

Hello World Imba SFC

Tiny template for testing out Imba SFC. The project is based on the hello-world-imba template.

This example uses vanilla CSS to show scoped and global examples which was introduced in Imba v1.5.1.

Getting started

git clone https://github.com/imba/hello-world-imba-sfc
cd hello-world-imba-sfc
yarn # npm install

You can run the app in two ways, either served via the webpack-dev-server or Express.

Webpack

# start webpack-dev-server and compiler
yarn run dev # npm run dev
Enter fullscreen mode Exit fullscreen mode

Server side

./node_modules/.bin/imba src/server.imba



If you find the boilerplate useful, please star it ⭐️ GitHub stars are appreciated also to our main repository 🙏🏾

GitHub logo imba / imba

🐤 The friendly full-stack language

install size Build Status Downloads PRs Welcome License

Imba is a friendly full-stack programming language for the web that compiles to performant JavaScript It has language-level support for defining, extending, subclassing, instantiating and rendering DOM nodes.

Get started

npx imba create hello-world
cd hello-world
npm start
Enter fullscreen mode Exit fullscreen mode

Documentation

To get started with Imba, we recommend reading through the official guide.

Why Imba?

Minimal syntax

Imba's syntax is minimal, beautiful, and packed with clever features. It combines logic, markup and styling in a powerful way. Fewer keystrokes and less switching files mean you'll be able to build things fast.

 "basics"

Runs on both server and client

Imba powers both the frontend and the backend of Scrimba.com, our learning platform with 100K+ monthly active users. On the frontend, Imba replaces e.g., Vue or React, and on the backend, it works with the Node ecosystem (e.g., npm).

 "server"

Integrated styling

Inspired by Tailwind, Imba brings styles directly into your code. Styles can be…

If you would like to keep up with the Imba development attend our Bi-Weekly Community meetings via zoom. For the next date see the last meeting notes.

Thank you for reading.

Discussion (3)

Collapse
itachiuchiha profile image
Itachi Uchiha

Thanks, Alexander. I loved this :)

Is there a pythonic way mm like imba.

Collapse
alexanderalemayhu profile image
Alexander Alemayhu Author • Edited on

I am still new to writing here so hopefully, the quality will improve over time, but glad you like it :D

Ahem... I don't really know Python web stuff that much other than Django, Brython and some other random projects. Unfortunately, I don't know of any projects similar to Imba in the Python ecosystem.

Thanks.