Syntax Guide
This section covers some common concepts of Abell's syntax, including examples of Loops, Conditions, and Abell Components.
Table of Contents
Basic Syntax
1. Hello World in Abell
Abell files are like HTML files but you can write JavaScript inside double curly brackets. This JavaScript code is executed and outputs a plain HTML file.
Input (.abell)
index.abell
{{
const a = 'Hello';
const b = ', World 🌻';
}}
<html>
<body>
I can render JavaScript! Look: {{ a + b.toUpperCase() }}
</body>
</html>
Output (.html)
index.html
<html>
<body>
I can render JavaScript! Look: Hello, WORLD 🌻
</body>
</html>
Notice how .toUpperCase()
(which is a native Node.js function) upper cased the "WORLD". Thus, you can use any Native Node.js functions inside double curly brackets.
2. Execute Maths
As we mentioned, you can write any Node.js code inside curly brackets including a code to add two variables!
{{
const a = 3;
const b = 9;
}}
<body>
{{ a + b }} <!-- Outputs: 12 -->
</body>
3. Conditions in Abell
{{
const isLink = true;
}}
<main>
{{
isLink
? /* html */ `<a href="https://google.com">Google</a>`
: /* html */ `<button>Do something!</button>`
}}
</main>
If the conditions are nested, writing a ternary operator is not ideal, it's recommended to create a separate function, return the value from the function and call that function inside double curly brackets.
Why /* html */
before the strings?
Ans: Adding /* html */
before the string, highlights the block as HTML content in our Abell Language Features VSCode Extension
4. Inlined Functions
Don't like ternary operator for conditions? No problem!
You can write a function that returns a value, and the value it returns is rendered in the template.
The same example from above, can be written as-
{{
const isLink = true;
}}
<main>
{{
() => {
if (isLink) {
return /* html */ `<a href="https://google.com">Google</a>`;
}
return /* html */ `<button>Do something!</button>`
}
}}
</main>
5. Loops in Abell
You can loop over content with Native JavaScript methods such as .map(). You can also use sort
, filter
and other array methods to customize the loop.
{{
const projects = [
{
title: 'Project 1',
desc: 'This is project 1'
},
{
title: 'Project 2',
desc: 'This is project 2'
}
]
}}
<main>
<section>
<h2>Projects</h2>
{{
projects.map(project => /* html */ `
<div>
<h3>${project.title}</h3>
<p>${project.desc}</p>
</div>
`)
}}
</section>
</main>
6. Escape Double Curly Brackets
If you want something to be printed as it is, you can escape double curly brackets by adding a forward slash before curly brackets \
7. Require JavaScript, NPM Packages, and JSON files
With Abell, you can use require()
to use JSON files, modules from other JavaScript files and NPM Packages with .abell
files.
{{
const moment = require('moment'); // NPM Package
const projects = require('./projects.json'); // JSON file
const calculate = require('./calculate.js'); // JS file
}}
It is a good practice to have all your requires on top before the <html>
tag.
This allows you to make use of Node.js ecosystem in various ways. For example you can use Node's dotenv package to read environment variables.
You can use remarkable to convert Markdown to HTML on the go!
Debugging in Abell
In Abell, you can log a variable with {{ console.log("Hi, I am printed on terminal") }}
. This message appears in your terminal (the one which is running the dev server).
{{ const a = 3; }}
<html>
<body>
{{ console.log(a) }} <!-- Printed on terminal -->
</body>
</html>
This will not effect your output HTML so in output it is rendered as a blank string.
Abell Pages have access to a special variable called Abell
. You can also print this variables to see what all functions and methods it has. (we will learn more about Abell
variable in API Reference)
theme/index.abell
<html>
{{ console.log(Abell) }}
</html>
Abell Components
Components are latest addition to Abell v0.4 🎉 Abell Components lets us wrap HTML, CSS, JavaScript which can be reused between Abell Pages.
Also since they are .abell
files as well, they can have double curly bracket blocks too! So everything we've seen so far in this section, is applicable for components as well.
Abell Components wrap <template>
, <style>
, and <script>
in <AbellComponent>
tag.
<AbellComponent>
<template>
<!-- HTML Content... -->
</template>
<style>
/* CSS for the HTML */
/*
styles are scoped by default.
You can add 'global' attribute to styles to make them global (not recommended)
*/
</style>
<script>
// Client-side JavaScript for the HTML
// Use scopedSelector instead of document.querySelector
// and scopedSelectorAll instead of document.querySelectorAll
// to ensure you always select element from same component.
</script>
</AbellComponent>
Important: <AbellComponent>
should always be in the first line. Everything else comes after it.
Example
It is a good practice to have components inside components
directory and their names to be in format TitleCased.abell
to differentiate them from Abell pages.
So a component for Footer would look like,
theme/components/Footer.abell
<AbellComponent>
<template>
<footer id="main-footer">
Built with Abell, <span id="year"></span>
</footer>
</template>
<style>
footer {
text-align: right;
padding: 10px;
}
</style>
<script>
// ensures I only select footer component that is inside this component.
scopedSelector('footer #year').innerText = new Date().getFullYear();
</script>
</AbellComponent>
Lets use this component in Abell Page (index.abell)
theme/index.abell
{{
const Footer = require('./components/Footer.abell');
}}
<html>
<body>
<!-- ... -->
<Footer/>
</body>
</html>
Note: <Footer></Footer>
is not currently valid! Self-closing tags (<Footer/>
) are the only way to write components right now.
Passing values to component
We can pass values to a component using props
attribute over the component.
theme/index.abell
<body>
<!-- ... -->
<Footer props={builtWith: 'Abell'} />
</body>
And we can access this value with {{ props.builtWith }}
in Footer Component.
theme/components/Footer.abell
<!-- ... -->
<template>
<footer id="main-footer">
Built with {{ props.builtWith }}, <span id="year"></span>
</footer>
</template>
<!-- ... -->
Note that this is only valid for props
attribute so you can't do <Footer foo={builtWith: 'Abell'} />
🙅
Bundling in Abell
Abell supports inlining styles and scripts to main page, as well as bundling it to separate CSS file and defining which file to bundle into.
On <script>
and <style>
tag inside Abell Component, you can use following bundling attributes-
inlined
to inline the content to the main page.bundle="something.css"
to bundle insidebundled-css/something.css
file on build.
When not defined, it is bundled into a common bundled-css/main.abell.css
and bundled-js/main.abell.js
file.
Example
1. Making CSS inlined and Adding JS to bundled-js/footer.js
theme/components/Footer.abell
<AbellComponent>
<template>
<footer id="main-footer">
Built with Abell, <span id="year"></span>
</footer>
</template>
<!-- this CSS will be added to <head> tag in parent page -->
<style inlined>
footer {
text-align: right;
padding: 10px;
}
</style>
<!-- This JavaScript will go into `bundled-js/footer.js` and <script src="bundled-js/footer.js"> will be added before end of <body> -->
<script bundle="footer.js">
scopedSelector('footer #year').innerText = new Date().getFullYear();
</script>
</AbellComponent>
2. Making JS inlined and Adding CSS to bundled-css/footer.css
theme/components/Footer.abell
<AbellComponent>
<template>
<footer id="main-footer">
Built with Abell, <span id="year"></span>
</footer>
</template>
<!--
This CSS will be added to bundled-css/footer.css
and It's <link rel="" /> will be added to parent page.
-->
<style bundle="footer.css">
footer {
text-align: right;
padding: 10px;
}
</style>
<!-- This JavaScript will be added to <script> tag before </body> -->
<script inlined>
scopedSelector('footer #year').innerText = new Date().getFullYear();
</script>
</AbellComponent>