tacl v0.1.2
Tech-Agnostic Component Library (TACL)
To create a component library using the basics (HTML, CSS, and JS), yet make them extendable into modern frontend libraries, like React, Angular, or VueJS. And because it's relying on the universal, basic web technology, it can also be used for other simpler web apps, like with a CMS.
Why
To share as much code between different frontend needs. Even if you have a static CMS site with limited JS, or a dynamic web app that's mostly JS code, you will still need UI components. Then your options are to do all the work twice or more, depending on your organization's needs, or you can consolidate the majority of your your code into one place.
Caveat: This is not for everyone, in fact, this is a niche problem. Most small companies will only have one tech stack, one component library, and so this would be an absolutely poor fit. However, with larger companies that have dozens of apps and teams and different tech stacks yet want to keep their UI more consistent and reduce maintenance, this might be of great value.
Architecture
A metaphor that can help think about this is instant ramen. The ramen noodles are the bulk of what's inside the packaging. It's universal and they sort of work alone but in actuality could use a flavor packet. The ramen noodles are your base library, while the specialized library would be the flavor packet. By making the noodles, you're 80% done with the work; now you only need to add some React, Angular, Vue or whatever flavor you get the remaining 20%.
Pros
- Reduce duplicated work and maintenance for component libraries.
- Work lasts longer. You can easily port your library to the new, trendy UI framework/library.
Cons
- A bit more initial work than if you were to only create one component library, like an Angular-only component library.
- Maintenance in Base Component Library needs a bit more thinking/caution to prevent issues downstream.
Base Library
./src
└───/components
└───/MyCustomComponent
│ └───/html
│ │ └───structure.html
│ │ └───example.html
│ │
│ └───/js
│ │ └───index.js (AUTO-GENERATED)
│ │ └───onClick.js
│ │ └───someHelperFunction.js
│ │
│ └───/sass
│ │ └───index.scss
│ │
│ └───/resources
│ └───some-icon.svg
│
└───/MyOtherComponent
/html
The base library is where you create your HTML structure files. These describe what HTML elements and classes you want to use. You can also comment the optional and conditional classes above the target element.
/sass
The only requirement with your CSS is to use an index.scss
file in the /sass
folder. You can from there @include
any additional SCSS files you want; if you're looking to organize with smaller files. The SCSS will autocompile to CSS for you.
/js
If the component has any functionality, like having state and updating it on a click, you can create functions to standardize how it handles those events. Please break them up by individual files for each function, using the same name. For example, if your exported function is called onClick
, then call your file onClick.js
.
Note: Please do not use export default
; it does not support this feature.
Here's an example:
// helloFoobar.js
import { sayHello } from './utils';
export function helloFoobar(foobar) {
return sayHello(foobar);
}
/*
Key Notes:
+ The exported variable is the same name as the file.
+ You can reference other non-exported functions/variables/classes from other files without issues.
- Not using "export default".
*/
Specialized Library
This is the tech-flavor packet to evolve the base library into a React component library (for example). It should mirror the Base Component Library but have all the importable code within src/components/<YOUR-COMPONENT>/tacl
folder.
Setup
Config Files
In each of your component libraries' repo, add a .taclrc
file.
In your Base Component Library repo:
{
"role": "base"
}
In your Specialized Component Library repo:
{
"role": "specialized",
"baseRepo": "https://github.com/your-username/your-base-repo.git"
}
Writing Base Component
- Create a new component under this structure:
base-repo/src/components/<YOUR-COMPONENT-NAME>
. - Add
base-repo/src/components/<YOUR-COMPONENT-NAME>/html/structure.html
file. - Write your semantic HTML.
- Add
base-repo/src/components/<YOUR-COMPONENT-NAME>/sass/index.scss
file. - Write your CSS using Sass.
- For each function you need, create a separate JS file under:
base-repo/src/components/<YOUR-COMPONENT-NAME>/js/<YOUR-FUNCTION-NAME>.js
. - Add JS code.
- Run the command
tacl compile
to ensure there's no errors. - Save files, commit, and push to
origin/master
.
Your base component code is now available to port into your Specialized Component Library.
Specializing a Component
From your Specialized Component Library, after it has been configured properly, you can run the command: tacl sync
.
This should scaffold your component library for you. It should essentially mirror your base component library but it places the code under a /tacl
folder instead, like: specialized-repo/src/components/<YOUR-COMPONENT-NAME>/tacl
.
For example, for React, you can create an index.js
file under specialized-repo/src/components/<YOUR-COMPONENT-NAME>/index.js
.
import React, { Component } from 'react';
// import styles and scripts for this component from the Base Component Library
import { onClick } from './tacl/scripts';
import './tacl/styles.css';
class MyCustomComponent extends Component {
state = {
count: 0,
};
onClick = () => {
const { state } = this;
const newState = onClick({ state }); // Using the 'onClick' imported from './tacl/scripts'...
this.setState(newState);
};
render () {
return (
<button
className="my-custom-component"
onClick={this.onClick}
>
{this.state.count}
</button>
);
}
}
export default MyCustomComponent;
This example shows that there's minimal work being done in the Specialized Component Library. The function, state management, styles, and elements rendered should all come from the Base Component Library. Essentially, we're only making the component usable within a React project.
This also means that there's little maintenance required within the Specialized Component Library. If there's an issue it most likely will be addressed within the Base Component Library.