├── src ├── bin │ ├── .gitignore │ └── App.ts ├── lib │ ├── .gitignore │ ├── browser │ │ └── Compiler.ts │ ├── Attributes.ts │ ├── Message.ts │ ├── Memories.ts │ └── Compiler.ts ├── test.ts ├── core │ ├── .gitignore │ ├── browser.ts │ └── node.ts ├── helpers │ ├── .gitignore │ └── minify.ts ├── constants │ ├── .gitignore │ ├── specialCharacters.ts │ ├── defaultConfig.ts │ ├── breakpointFormat.ts │ └── standardSize.ts ├── scripts │ ├── .gitignore │ ├── css_display.ts │ ├── css_alignItems.ts │ ├── css_justifyContent.ts │ ├── css_gridTemplateColumns.ts │ ├── css_flex.ts │ ├── css_flexDirection.ts │ ├── css_columns.ts │ ├── css_flexWrap.ts │ ├── css_aspectRatio.ts │ ├── css_zIndex.ts │ ├── css_break.ts │ ├── css_boxDecoration.ts │ ├── css_borderRadius.ts │ ├── css_flexBasis.ts │ ├── css_gap.ts │ └── css_paddingAndMargin.ts ├── utils │ ├── parsePropertyValues.ts │ ├── flexer.ts │ ├── addSlashes.ts │ ├── propertiesParser.ts │ ├── watcher.ts │ ├── breakpointParser.ts │ ├── functions.ts │ └── cssParser.ts ├── global.d.ts ├── index.ts └── init.ts ├── docs └── .gitignore ├── scripts ├── .gitignore ├── bundle.js ├── lib │ └── Message.js └── utilities │ └── functions.js ├── bin └── fisay ├── sass ├── components │ ├── utilities │ │ ├── colors.scss │ │ └── mixins.scss │ ├── Card.scss │ └── Form.scss ├── components.scss └── includes │ ├── breakpoint.scss │ └── reset.scss ├── nodemon.json ├── __test__ ├── fisay.config.json ├── about.html ├── index.html └── css │ ├── out.css.map │ └── out.css ├── .gitignore ├── .github ├── dependabot.yml └── FUNDING.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── package.json ├── CODE_OF_CONDUCT.md └── tsconfig.json /src/bin/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/lib/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // todo -------------------------------------------------------------------------------- /src/core/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/helpers/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore -------------------------------------------------------------------------------- /src/constants/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/scripts/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | *.d.ts -------------------------------------------------------------------------------- /scripts/.gitignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | *.d.ts 3 | !.gitignore -------------------------------------------------------------------------------- /bin/fisay: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../build/src/index.js') -------------------------------------------------------------------------------- /sass/components/utilities/colors.scss: -------------------------------------------------------------------------------- 1 | $slate: #adc7cd; 2 | $gray: #949494; -------------------------------------------------------------------------------- /src/lib/browser/Compiler.ts: -------------------------------------------------------------------------------- 1 | export default class Compiler { 2 | constructor () { 3 | 4 | } 5 | } -------------------------------------------------------------------------------- /src/constants/specialCharacters.ts: -------------------------------------------------------------------------------- 1 | const specialCharacters = [ 2 | ":", 3 | "[", 4 | "]" 5 | ]; 6 | 7 | export = specialCharacters; -------------------------------------------------------------------------------- /src/utils/parsePropertyValues.ts: -------------------------------------------------------------------------------- 1 | export default function parsePropertyValues (value) { 2 | return `${(value * 0.2).toFixed(2)}rem`; 3 | } -------------------------------------------------------------------------------- /nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": [ 3 | "src/**/*", 4 | "mixin/*.scss" 5 | ], 6 | "ext": ".ts", 7 | "ignore": [], 8 | "exec": "tsc --build" 9 | } -------------------------------------------------------------------------------- /sass/components/utilities/mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin FlexBox($direction: row, $gap: 2) { 2 | display: flex; 3 | flex-direction: $direction; 4 | gap: $gap; 5 | } -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | var blob: string; 3 | var memory: object; 4 | var fileSass: string; 5 | var pwd: string; 6 | var config: object; 7 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import App from "./bin/App"; 2 | 3 | if (typeof process === "undefined") throw new Error("sorry, not supported via browser"); 4 | 5 | let app = new App("node"); 6 | app.start(); -------------------------------------------------------------------------------- /src/utils/flexer.ts: -------------------------------------------------------------------------------- 1 | const flexer = (blob: string) => { 2 | return blob.replace(/(undefined)/g, "").replace(/\{/g, "{\n").replace(/\;/g, ";\n").replace(/\}/g, "}\n"); 3 | } 4 | 5 | export = flexer; -------------------------------------------------------------------------------- /src/constants/defaultConfig.ts: -------------------------------------------------------------------------------- 1 | export = { 2 | minified: false, 3 | input: "./", 4 | output: "./css/output.css", 5 | sourceMap: true, 6 | watch: false, 7 | allowedExtension: ["js", "html", "php", "jsx"] 8 | } -------------------------------------------------------------------------------- /sass/components/Card.scss: -------------------------------------------------------------------------------- 1 | @import "./utilities/mixins"; 2 | @import "./utilities/colors"; 3 | 4 | @mixin Card () { 5 | @include FlexBox(column, 2); 6 | padding: .2rem; 7 | border: solid .5px $gray; 8 | @content 9 | } -------------------------------------------------------------------------------- /sass/components/Form.scss: -------------------------------------------------------------------------------- 1 | @import "./utilities/mixins"; 2 | @import "./utilities/colors"; 3 | 4 | 5 | @mixin FormControl () { 6 | 7 | } 8 | 9 | 10 | @mixin InputGroup () { 11 | @include FlexBox(row, 2); 12 | } -------------------------------------------------------------------------------- /src/constants/breakpointFormat.ts: -------------------------------------------------------------------------------- 1 | const breakpointFormat = { 2 | xs: "verySmall", 3 | sm: "small", 4 | md: "medium", 5 | lg: "large", 6 | xl: "veryLarge", 7 | xxl: "superLarge" 8 | } 9 | 10 | export = breakpointFormat; -------------------------------------------------------------------------------- /__test__/fisay.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "minified": false, 3 | "input": "./", 4 | "output": "./css/output.css", 5 | "watch": false, 6 | "allowedExtension": [ 7 | "js", 8 | "html", 9 | "php", 10 | "jsx" 11 | ] 12 | } -------------------------------------------------------------------------------- /sass/components.scss: -------------------------------------------------------------------------------- 1 | @import "./components/Card"; 2 | @import "./components/Form"; 3 | 4 | .card { 5 | @include Card; 6 | } 7 | 8 | .form-control { 9 | @include FormControl; 10 | } 11 | 12 | .input-group { 13 | @include InputGroup; 14 | } -------------------------------------------------------------------------------- /src/helpers/minify.ts: -------------------------------------------------------------------------------- 1 | const minify = (filecontent: any) => { 2 | if (!filecontent) return; 3 | return filecontent 4 | .replace(/\/\s?.*\s?\//g, '') // delete comments 5 | .replace(/\n/g, '') 6 | .replace(/\s\s+/g, ' '); 7 | } 8 | 9 | export = minify; -------------------------------------------------------------------------------- /src/core/browser.ts: -------------------------------------------------------------------------------- 1 | const pkg = require("../../package.json"); 2 | 3 | if (typeof process !== "undefined") throw new Error("sorry, not supported via node") 4 | 5 | const compiler = require("../lib/browser/Compiler"); 6 | document.addEventListener("DOMContentLoaded", function(){ 7 | 8 | }) -------------------------------------------------------------------------------- /scripts/bundle.js: -------------------------------------------------------------------------------- 1 | const pkg = require("../package.json"); 2 | const path = require("path"); 3 | const { verifyPath, compile } = require("./utilities/functions.js"); 4 | 5 | verifyPath(); 6 | 7 | const __file__ = path.resolve(__dirname, "../sass/components.scss"); 8 | const __outdir__ = path.resolve(__dirname, "../dist/css"); 9 | compile(__file__, __outdir__); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /node_modules 3 | /public/hot 4 | /public/storage 5 | /storage/*.key 6 | /vendor 7 | /__pycache__ 8 | .env 9 | .env.backup 10 | .env.local 11 | .phpunit.result.cache 12 | docker-compose.override.yml 13 | Homestead.json 14 | Homestead.yaml 15 | npm-debug.log 16 | yarn-error.log 17 | /.idea 18 | /.vscode 19 | /.esdoc.json 20 | 21 | # autogit config 22 | /.autogit -------------------------------------------------------------------------------- /src/scripts/css_display.ts: -------------------------------------------------------------------------------- 1 | const css_display = (attr: string) => { 2 | const format = /^(flex|block|grid|table|inline-table|inline-block)$/ 3 | let match = attr.match(format); 4 | 5 | if (!match) return false; 6 | const [selector, property] = match; 7 | return { 8 | selector: selector, 9 | value: `display: ${ property };` 10 | } 11 | 12 | } 13 | export = css_display; -------------------------------------------------------------------------------- /src/utils/addSlashes.ts: -------------------------------------------------------------------------------- 1 | import specialCharacters from "../constants/specialCharacters"; 2 | 3 | const addSlashes = (str: string) => { 4 | let text = str.replace(/\:/, "\\:") 5 | .replace(/\]/, "\\]") 6 | .replace(/\[/, "\\[") 7 | .replace("/", "\\/") 8 | .replace(/\./, "\\.") 9 | return text; 10 | } 11 | export = addSlashes; -------------------------------------------------------------------------------- /src/scripts/css_alignItems.ts: -------------------------------------------------------------------------------- 1 | const css_alignItems = (attr: string) => { 2 | const format = /^(items)-(center|stretch|start|end|evenly)/ 3 | let match = attr.match(format); 4 | if (!match) return false; 5 | 6 | const [selector,, value] = match; 7 | return { 8 | selector: selector, 9 | value: `align-items: ${ value };` 10 | } 11 | 12 | } 13 | 14 | export = css_alignItems; -------------------------------------------------------------------------------- /src/bin/App.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | 4 | import node from "../core/node"; 5 | // import browser from "../core/browser"; 6 | 7 | export default class App { 8 | private type: string; 9 | 10 | constructor(type: string) { 11 | this.type = type; 12 | } 13 | 14 | public start () { 15 | /* initialize */ 16 | 17 | node.parse(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/scripts/css_justifyContent.ts: -------------------------------------------------------------------------------- 1 | const css_justifyContent = (attr: string) => { 2 | const format = /^(justify)-(around|center|between|start|end)?/ 3 | let match = attr.match(format); 4 | 5 | if (!match) return false; 6 | const [selector,, property] = match; 7 | return { 8 | selector: selector, 9 | value: `justify-content: ${ property };` 10 | } 11 | 12 | } 13 | 14 | export = css_justifyContent; -------------------------------------------------------------------------------- /src/scripts/css_gridTemplateColumns.ts: -------------------------------------------------------------------------------- 1 | const css_gridTemplateColumns = (attr: string) => { 2 | const format = /^(grid-cols)-(\d)+/ 3 | let match = attr.match(format); 4 | 5 | if (!match) return false; 6 | const [selector,, value] = match; 7 | return { 8 | selector: selector, 9 | value: `grid-template-columns: repeat(${ value }, 1fr);` 10 | } 11 | 12 | } 13 | 14 | export = css_gridTemplateColumns; -------------------------------------------------------------------------------- /src/lib/Attributes.ts: -------------------------------------------------------------------------------- 1 | export default class { 2 | private attributes: string[]; 3 | public constructor() { 4 | this.attributes = []; 5 | } 6 | 7 | private push(attr: string) { 8 | if (this.attributes?.includes(attr)) return false; 9 | 10 | this.attributes?.push(attr); 11 | } 12 | 13 | public add(attr: string[]) { 14 | attr.forEach(v => this.push(v)) 15 | } 16 | 17 | public getAttributes() { 18 | return this.attributes; 19 | } 20 | } -------------------------------------------------------------------------------- /src/scripts/css_flex.ts: -------------------------------------------------------------------------------- 1 | import { FLEX_PREFIX_VALUES } from "../constants/standardSize"; 2 | 3 | const css_flexWrap = (attr: string) => { 4 | const format = new RegExp(`^flex-(${ Object.keys(FLEX_PREFIX_VALUES).join("|") })$`); 5 | let match = attr.match(format); 6 | if (!match) return false; 7 | const [selector, prefix] = match; 8 | 9 | return { 10 | selector: selector, 11 | value: `flex: ${FLEX_PREFIX_VALUES[prefix]}` 12 | } 13 | } 14 | export = css_flexWrap; -------------------------------------------------------------------------------- /src/scripts/css_flexDirection.ts: -------------------------------------------------------------------------------- 1 | import { FLEX_DIRECTION_PREFIX_VALUES } from "../constants/standardSize"; 2 | 3 | const css_flexDirection = (attr: string) => { 4 | const format = /flex\-((row|col)(\-(reverse))?)/; 5 | let match = attr.match(format); 6 | if (!match) return false; 7 | const [selector, prefix] = match; 8 | 9 | return { 10 | selector: selector, 11 | value: `flex-direction: ${FLEX_DIRECTION_PREFIX_VALUES[prefix]}` 12 | } 13 | } 14 | export = css_flexDirection; -------------------------------------------------------------------------------- /src/utils/propertiesParser.ts: -------------------------------------------------------------------------------- 1 | import { scripts } from "../init"; 2 | import addSlashes from "../utils/addSlashes"; 3 | 4 | const propertiesParser = (attr: string) => { 5 | let syntax = ""; 6 | for (let key in scripts) { 7 | let res = scripts[key](attr); 8 | if (res) { 9 | syntax += `.${ addSlashes(res.selector) } {` 10 | + `${ res.value }` 11 | + `}` 12 | globalThis.blob += syntax; 13 | } 14 | } 15 | 16 | } 17 | 18 | export = propertiesParser; -------------------------------------------------------------------------------- /src/scripts/css_columns.ts: -------------------------------------------------------------------------------- 1 | import { COLUMNS_PREFIX_VALUES } from "../constants/standardSize"; 2 | 3 | const css_columns = (attr: string) => { 4 | const format = new RegExp(`^columns-(${ Object.keys(COLUMNS_PREFIX_VALUES).join("|") })`); 5 | let match = attr.match(format); 6 | 7 | if (!match) return false; 8 | const [selector, prefix] = match; 9 | 10 | return { 11 | selector: selector, 12 | value: `columns: ${COLUMNS_PREFIX_VALUES[prefix]}` 13 | } 14 | } 15 | export = css_columns; -------------------------------------------------------------------------------- /src/scripts/css_flexWrap.ts: -------------------------------------------------------------------------------- 1 | import { FLEX_WRAP_PREFIX_VALUES } from "../constants/standardSize"; 2 | 3 | const css_flexWrap = (attr: string) => { 4 | const format = new RegExp(`^flex-(${ Object.keys(FLEX_WRAP_PREFIX_VALUES).join("|") })$`); 5 | let match = attr.match(format); 6 | if (!match) return false; 7 | const [selector, prefix] = match; 8 | 9 | return { 10 | selector: selector, 11 | value: `flex-wrap: ${FLEX_WRAP_PREFIX_VALUES[prefix]}` 12 | } 13 | } 14 | export = css_flexWrap; -------------------------------------------------------------------------------- /src/scripts/css_aspectRatio.ts: -------------------------------------------------------------------------------- 1 | import { ASPECT_PREFIX_VALUES } from "../constants/standardSize"; 2 | 3 | const css_aspectRatio = (attr: string) => { 4 | const format = new RegExp(`^aspect\-(${ Object.keys(ASPECT_PREFIX_VALUES).join("|") })$`); 5 | let match = attr.match(format); 6 | 7 | if (!match) return false; 8 | const [selector, prefix] = match; 9 | 10 | return { 11 | selector: selector, 12 | value: `aspect-ratio: ${ ASPECT_PREFIX_VALUES[prefix] }` 13 | } 14 | } 15 | export = css_aspectRatio; -------------------------------------------------------------------------------- /src/scripts/css_zIndex.ts: -------------------------------------------------------------------------------- 1 | import { Z_INDEX_VALUES } from "../constants/standardSize"; 2 | 3 | const css_zIndex = (attr: string) => { 4 | const format = new RegExp(`^z-(\[(\d+)\]|((${ Z_INDEX_VALUES.join("|") })))$`); 5 | let match = attr.match(format); 6 | 7 | if (!match) return false; 8 | const [selector, property, customValue] = match; 9 | 10 | return { 11 | selector: selector, 12 | value: `z-index: ${ /(\[|\])/.test(property) ? customValue : property };` 13 | } 14 | } 15 | export = css_zIndex; -------------------------------------------------------------------------------- /src/scripts/css_break.ts: -------------------------------------------------------------------------------- 1 | import { BREAK_PREFIX_VALUES } from "../constants/standardSize"; 2 | 3 | const css_break = (attr: string) => { 4 | const format = new RegExp(`^break-(after|before|inside)-(${ Object.keys(BREAK_PREFIX_VALUES).join("|") })`); 5 | let match = attr.match(format); 6 | 7 | if (!match) return false; 8 | const [selector, prefix, value] = match; 9 | 10 | return { 11 | selector: selector, 12 | value: `break-${prefix}: ${BREAK_PREFIX_VALUES[value]}` 13 | } 14 | } 15 | export = css_break; -------------------------------------------------------------------------------- /src/utils/watcher.ts: -------------------------------------------------------------------------------- 1 | import observer from "node-watch"; 2 | import path from "path"; 3 | import message from "../lib/Message"; 4 | 5 | const watcher = async (pathTarget: string, callback: Function) => { 6 | callback(); 7 | message.info("watching file changed..."); 8 | observer(pathTarget, { 9 | filter: /\.(htm(l)|js|jsx|vue|php)$/, 10 | recursive: true 11 | }, function(event, filename){ 12 | console.log(`${ path.basename(filename) } ${event}`) 13 | callback() 14 | }); 15 | } 16 | 17 | export = watcher -------------------------------------------------------------------------------- /src/scripts/css_boxDecoration.ts: -------------------------------------------------------------------------------- 1 | import { BREAK_DECORATION_PREFIX_VALUES } from "../constants/standardSize"; 2 | 3 | const css_break = (attr: string) => { 4 | const format = new RegExp(`^box-decoration-(${ Object.keys(BREAK_DECORATION_PREFIX_VALUES).join("|") })`); 5 | let match = attr.match(format); 6 | 7 | if (!match) return false; 8 | const [selector, prefix] = match; 9 | 10 | return { 11 | selector: selector, 12 | value: `box-decoration-${prefix}: ${BREAK_DECORATION_PREFIX_VALUES[prefix]}` 13 | } 14 | } 15 | export = css_break; -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /src/scripts/css_borderRadius.ts: -------------------------------------------------------------------------------- 1 | import { BORDER_PREFIX_VALUES, WAY_PRERIX_VALUES } from "../constants/standardSize"; 2 | 3 | const css_borderRadius = (attr: string) => { 4 | const format = /^rounded\-?(t|r|b|l)?\-((xs|sm|md|lg|xl|xxl|full)|\[(\w+)\])/; 5 | let match = attr.match(format); 6 | if (!match) return false; 7 | const [selector, way, prefix, value, customValue] = match; 8 | 9 | return { 10 | selector: selector, 11 | value: `border${ way ? "-" + WAY_PRERIX_VALUES[way] : "" }: ${ /(\[|\])/.test(selector) ? customValue : BORDER_PREFIX_VALUES[prefix] };` 12 | } 13 | } 14 | 15 | 16 | export = css_borderRadius; -------------------------------------------------------------------------------- /src/scripts/css_flexBasis.ts: -------------------------------------------------------------------------------- 1 | import { FLEX_BASIS_PREFIX_VALUES } from "../constants/standardSize"; 2 | import parsePropertyValues from "../utils/parsePropertyValues"; 3 | 4 | const css_flexBasis = (attr: string) => { 5 | const format = new RegExp(`^basis-(${ Object.keys(FLEX_BASIS_PREFIX_VALUES).join("|") })$`); 6 | let match = attr.match(format); 7 | 8 | if (!match) return false; 9 | const [selector, value, customValue] = match; 10 | 11 | return { 12 | selector: selector, 13 | value: `flex-basis: ${ /(\[|\])/.test(value) ? customValue : (typeof value === "number" ? parsePropertyValues(FLEX_BASIS_PREFIX_VALUES[value]) : FLEX_BASIS_PREFIX_VALUES[value]) };` 14 | } 15 | } 16 | export = css_flexBasis; -------------------------------------------------------------------------------- /__test__/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 |
12 |
ok
13 |
14 | 15 |
16 |
17 | 18 | -------------------------------------------------------------------------------- /src/scripts/css_gap.ts: -------------------------------------------------------------------------------- 1 | import { PREFIX_VALUES } from "../constants/standardSize"; 2 | import parsePropertyValues from "../utils/parsePropertyValues"; 3 | 4 | const css_gap = (attr: string) => { 5 | const format = new RegExp(`^gap\-?(y|x)?\-(\\[(\\w+)\\]|(((${ PREFIX_VALUES.join("|") })+)))$`); 6 | let match = attr.match(format); 7 | 8 | if (!match) return false; 9 | const [selector, prefix, value, customValue] = match; 10 | 11 | let property = "gap"; 12 | if (prefix && prefix === "x") property = "row-gap"; 13 | if (prefix && prefix === "y") property = "column-gap"; 14 | 15 | return { 16 | selector: selector, 17 | value: `${property}: ${ /(\[|\])/.test(value) ? customValue : parsePropertyValues(value) };` 18 | } 19 | } 20 | export = css_gap; -------------------------------------------------------------------------------- /scripts/lib/Message.js: -------------------------------------------------------------------------------- 1 | const chalk = require("chalk"); 2 | 3 | class Message { 4 | generate(text, color, option = "normal") { 5 | return option == "normal" ? chalk.hex(color)(text) : chalk.hex(color)[option](text); 6 | } 7 | 8 | console (text) { 9 | console.log(text); 10 | } 11 | 12 | danger (text) { 13 | this.console( this.generate(text, "#f93b00") ); 14 | process.exit(); 15 | } 16 | 17 | info (text) { 18 | this.console( this.generate(text, "#0798ff") ); 19 | } 20 | 21 | warning (text) { 22 | this.console( this.generate(text, "#feff08") ); 23 | } 24 | 25 | success (text) { 26 | this.console( this.generate(text, "#08b835") ); 27 | } 28 | } 29 | 30 | const message = new Message(); 31 | 32 | module.exports = { 33 | Message, 34 | message 35 | }; -------------------------------------------------------------------------------- /src/lib/Message.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | 3 | class Message { 4 | public generate(text: string, color: string, option: string = "normal") { 5 | return option == "normal" ? chalk.hex(color)(text) : chalk.hex(color)[option](text); 6 | } 7 | 8 | public console (text: string) { 9 | console.log(text); 10 | } 11 | 12 | public danger (text: string) { 13 | this.console( this.generate(text, "#f93b00") ); 14 | process.exit(); 15 | } 16 | 17 | public info (text: string) { 18 | this.console( this.generate(text, "#0798ff") ); 19 | } 20 | 21 | public warning (text: string) { 22 | this.console( this.generate(text, "#feff08") ); 23 | } 24 | 25 | public success (text: string) { 26 | this.console( this.generate(text, "#08b835") ); 27 | } 28 | } 29 | 30 | 31 | export = new Message; -------------------------------------------------------------------------------- /src/utils/breakpointParser.ts: -------------------------------------------------------------------------------- 1 | import { scripts } from "../init"; 2 | import propertiesParser from "./propertiesParser"; 3 | import addSlashes from "./addSlashes"; 4 | import breakpointFormat from "../constants/breakpointFormat"; 5 | 6 | const breakpointParser = (breakpoint: string, items: string[]) => { 7 | const device = breakpointFormat[breakpoint] ? breakpointFormat[breakpoint] : "small"; 8 | let syntax = ""; 9 | syntax += `@include devices(${ device }) {` 10 | for (let item of items) { 11 | for (let key in scripts) { 12 | let res = scripts[key](item); 13 | if (res) { 14 | let selector = addSlashes(`${breakpoint}:${res.selector}`); 15 | syntax += `.${ selector } {` 16 | + `${ res.value }` 17 | + `}` 18 | } 19 | } 20 | } 21 | syntax += "}"; 22 | globalThis.blob += syntax; 23 | } 24 | 25 | export = breakpointParser; -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: fiandev # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: fiandev # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: ['https://saweria.co/fiandev'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /src/scripts/css_paddingAndMargin.ts: -------------------------------------------------------------------------------- 1 | import { WAY_PRERIX_VALUES, PREFIX_VALUES } from "../constants/standardSize"; 2 | import parsePropertyValues from "../utils/parsePropertyValues"; 3 | 4 | const css_paddingAndMargin = (attr: string) => { 5 | const format = new RegExp(`^(p|m)(t|r|b|l|x|y)?\-(((${ PREFIX_VALUES.join("|") })+)|\\[(\\w+)\\])`); 6 | let match = attr.match(format); 7 | if (!match) return false; 8 | const [selector, prefix, way,, value,, customValue] = match; 9 | 10 | let result = `${ /(\[|\])/.test(selector) ? customValue : parsePropertyValues(value) }`; 11 | 12 | return { 13 | selector: selector, 14 | value: `${prefix === "p" ? "padding" : "margin"}${ way ? ( WAY_PRERIX_VALUES[way] ? "-" + WAY_PRERIX_VALUES[way] : "" ) : "" }: ${ way === "x" || way === "y" ? (way === "x" ? "0 " + result : result + " 0") : result };` 15 | } 16 | } 17 | 18 | 19 | export = css_paddingAndMargin; -------------------------------------------------------------------------------- /sass/includes/breakpoint.scss: -------------------------------------------------------------------------------- 1 | /* the name of the mixin is devices */ 2 | @mixin devices ($breakpoint) { 3 | 4 | @if $breakpoint == verySmall { 5 | @media only screen and (min-width: 0px) { 6 | @content; 7 | } 8 | } 9 | @if $breakpoint == small { 10 | @media only screen and (max-width: 575px) { 11 | @content; 12 | } 13 | } 14 | 15 | @if $breakpoint == medium { 16 | @media only screen and (min-width: 576px) { 17 | @content; 18 | } 19 | } 20 | 21 | @if $breakpoint == large { 22 | @media only screen and (min-width: 992px) { 23 | @content; 24 | } 25 | } 26 | 27 | @if $breakpoint == veryLarge { 28 | @media only screen and (min-width: 1200px) { 29 | @content; 30 | } 31 | } 32 | 33 | @if $breakpoint == superLarge { 34 | @media only screen and (min-width: 1400px) { 35 | @content; 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # how to contribute ? 2 | 3 | - fork this repository 4 | - create new branch in your repository 5 | - add the updates you made 6 | - pull request your branch 7 | - wait until your pull request accepted 8 | 9 | > thanks for your contribution 😊 10 | 11 | # structure folders 12 | 13 | - `~/src` 14 | the main folder for storing programs with the extension **.ts** 15 | - `~/build` 16 | output folder from compiling typescript 17 | - `~/dist` 18 | output folder from compiling `~/sass/components.scss`, This is used to create utility class components like a boostrap components 19 | - `~/src/scripts` 20 | the main folder for store functions to parsing utilities class like tailwind 21 | - `~/src/constants` 22 | folder to store important variables of program 23 | - `~/__test__` 24 | folder for store result of testing program 25 | - `~/scripts` 26 | folder for storing programs that run via scripts in `package.json` 27 | - `~/docs` 28 | folder to store the documentation files 29 | 30 | -------------------------------------------------------------------------------- /src/utils/functions.ts: -------------------------------------------------------------------------------- 1 | export const getPrimeNumbers = (each: number = 10, divider: number = 2) => { 2 | var result = []; 3 | for (let i = 0; i < each; i++) { 4 | if (i % divider === 0 || i === 1) result.push(i); 5 | else continue; 6 | } 7 | 8 | return result; 9 | } 10 | 11 | 12 | export const ArrayToObject = (arr: any[]) => { 13 | let result: object = {}; 14 | arr.forEach(val => { 15 | if (!result[val]) result[val] = val; 16 | }) 17 | 18 | return result 19 | } 20 | 21 | export const getIterateNumbers = (start: number, end: number) => { 22 | let result: number[] = []; 23 | for (let i = 0; i < end; i++) { 24 | let value = start + i; 25 | result.push(value) 26 | } 27 | 28 | return result; 29 | } 30 | 31 | export const getFractions = (denominator: number) => { 32 | let result = {}; 33 | for (let i = 1; i < denominator; i++) { 34 | let fraction = `${i}/${denominator}`; 35 | result[fraction] = eval(fraction) * 100 + "%"; 36 | } 37 | 38 | return result; 39 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 fiandev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/lib/Memories.ts: -------------------------------------------------------------------------------- 1 | export default class { 2 | public constructor(attrClass: string) { 3 | let attrs = attrClass.split(/\s/) 4 | for (let attr of attrs) { 5 | 6 | let isBreakPoint = this.isBreakPoint(attr); 7 | let isNormalScope = this.isNormalScope(attr); 8 | 9 | if (isBreakPoint) { 10 | let [captures, breakpoint, scopes] = isBreakPoint; 11 | if (!globalThis.memory[breakpoint]) globalThis.memory[breakpoint] = []; 12 | this.push(breakpoint, scopes.split(" ")); 13 | } 14 | 15 | if (isNormalScope) { 16 | if (!globalThis.memory["normal"]) globalThis.memory["normal"] = []; 17 | this.push("normal", attr.split(" ")); 18 | 19 | } 20 | 21 | } 22 | } 23 | 24 | private isBreakPoint(text: any) { 25 | return text.match(/\s?(\w+):\s?((\w|\-|\S)+)/); 26 | } 27 | 28 | private isNormalScope(text: any) { 29 | return !/\s?(\w+):\s?((\w|\-|\s)+)/.test(text); 30 | } 31 | 32 | private push(key: string, items: string[]) { 33 | for (let item of items) { 34 | if (!globalThis.memory[key].includes(item)) globalThis.memory[key].push(item); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fisay css 2 | A framework css like a tailwind CSS and have utilities components like boostrap. 3 | 4 | -------------- 5 | 6 | # how to usage 7 | 8 | ## installation 9 | 10 | ### clone repository 11 | 12 | ```shell 13 | # clone git repository 14 | git clone https://github.com/fiandev/fisay 15 | 16 | # entering repository project 17 | cd fisay 18 | 19 | # install required dependencies 20 | npm install 21 | 22 | # run npm ```npm link``` 23 | 24 | npm link 25 | ``` 26 | 27 | ### install from npm 28 | 29 | ```shell 30 | # install in global 31 | 32 | npm install fisay -g 33 | ``` 34 | 35 | ## compile file html to css 36 | 37 | input can be path of file or directory. 38 | 39 | 40 | ```shell 41 | # example command 42 | fisay compile -i -o [options] 43 | 44 | # example 45 | fisay compile -i ./index.html -o ./output/style.css --watch 46 | 47 | # watcher 48 | fisay compile -i -o -w 49 | 50 | # show list of commands 51 | fisay --help 52 | 53 | # init configuration 54 | fisay init 55 | ``` 56 | 57 | ## Documentation 58 | 59 | comming soon! 60 | 61 | ## Community 62 | 63 | comming soon! 64 | 65 | ## Contributing 66 | 67 | If you're interested in contributing to fisay CSS, please read our [contributing docs](https://github.com/fiandev/fisay/blob/master/CONTRIBUTING.md) **before submitting a pull request**. -------------------------------------------------------------------------------- /__test__/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document Testing 8 | 9 | 10 | 11 |
owlah iyo :v
12 | 13 |
14 |
item
15 |
item
16 |
item
17 |
item
18 |
item
19 |
item
20 |
item
21 |
item
22 |
item
23 |
24 | 25 | -------------------------------------------------------------------------------- /scripts/utilities/functions.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const sass = require("sass"); 4 | const postcss = require("postcss"); 5 | const autoprefixer = require("autoprefixer"); 6 | const pkg = require("../../package.json"); 7 | const { message } = require("../lib/Message"); 8 | 9 | const MESSAGE_EXCEPTION = "the script must be run at the root of the program, see package.json scripts to find out which scripts are available"; 10 | let pwd = path.join(process.cwd(), "./"); 11 | let root = path.join(__dirname, "../../"); 12 | 13 | module.exports = { 14 | verifyPath, 15 | compile, 16 | prefixer, 17 | message 18 | }; 19 | 20 | function verifyPath () { 21 | const IsValid = pwd === root; 22 | if (!IsValid) { 23 | message.danger(MESSAGE_EXCEPTION); 24 | process.exit(); 25 | } 26 | } 27 | 28 | 29 | async function compile (file, outdir) { 30 | const filename = `${pkg.name}.bundle` || "bundle"; 31 | 32 | fs.writeFileSync(`${outdir}/${filename}.min.css`, await compileFileScss(file, "compressed")); 33 | fs.writeFileSync(`${outdir}/${filename}.css`, await compileFileScss(file, "expanded")); 34 | 35 | console.log(`success bundling file ${path.basename(file)}`); 36 | } 37 | 38 | 39 | async function compileFileScss (file, style) { 40 | const compiled = sass.compile(file, { 41 | sourceMap: false, 42 | style: style 43 | }); 44 | 45 | return await prefixer(compiled.css).css; 46 | } 47 | 48 | async function prefixer() { 49 | return await postcss([ autoprefixer(pkg.browserslist.production) ]).process(compiled.css); 50 | } -------------------------------------------------------------------------------- /src/init.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import * as pJson from '../package.json'; 4 | import minify from "./helpers/minify"; 5 | import defaultConfig from "./constants/defaultConfig" 6 | import message from "./lib/Message"; 7 | 8 | const path_sass_includes = "/../../sass/includes"; 9 | 10 | export const pkg = pJson; 11 | export const scripts = require('require-all')({ 12 | dirname: path.join(__dirname, "/scripts"), 13 | recursive: true 14 | }); 15 | 16 | const files = fs.readdirSync(path.join(__dirname, path_sass_includes)); 17 | 18 | /* declare global blob */ 19 | let blob = ""; 20 | 21 | /* includes mixin scss files */ 22 | for (let file of files) { 23 | blob += fs.readFileSync(path.join(__dirname, `${path_sass_includes}/${ file }`)); 24 | } 25 | 26 | export const scssBlob = minify(blob); 27 | 28 | let pwd = process.cwd(); 29 | let pathfile = path.join(pwd, `./${pkg.name}.config.json`); 30 | let isFileConfigExist = fs.existsSync(pathfile); 31 | let fileConfig = isFileConfigExist ? fs.readFileSync(pathfile, "utf8") : JSON.stringify(defaultConfig); 32 | 33 | if (isFileConfigExist) message.warning(`file config is exist at ./${path.basename(pathfile)}, use it!`); 34 | else message.info("file config not found, use default configuration!"); 35 | 36 | try { 37 | let config = JSON.parse(fileConfig); 38 | 39 | /* load & parse config */ 40 | for (let key in defaultConfig) { 41 | if (typeof config[key] === "undefined") config[key] = defaultConfig[key]; 42 | } 43 | 44 | /* passing config */ 45 | globalThis.config = config; 46 | } catch (err) { 47 | message.danger("invalid file config, please check your file config!"); 48 | process.exit(); 49 | } -------------------------------------------------------------------------------- /src/utils/cssParser.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import sass from "sass"; 4 | import autoprefixer from "autoprefixer"; 5 | import postcss from "postcss"; 6 | import child_process from "child_process"; 7 | import Message from "../lib/Message"; 8 | import { pkg } from "../init"; 9 | 10 | const executeParser = ({ syntax, output, outdir }) => { 11 | let filename = path.basename(output); 12 | const compiled = sass.compileString(syntax, { 13 | sourceMap: globalThis.config.sourceMap, 14 | style: globalThis.config.minified ? "compressed" : "expanded" 15 | }); 16 | 17 | postcss([ autoprefixer(pkg.browserslist.production) ]) 18 | .process(compiled.css) 19 | .then(function(result) { 20 | fs.writeFileSync(output, result.css); 21 | }) 22 | 23 | if (compiled["sourceMap"]) fs.writeFileSync(`${outdir}/${filename}.map`, JSON.stringify(compiled.sourceMap, null, 2)); 24 | 25 | Message.success(`success compiled to ${outdir}/${filename}`); 26 | } 27 | 28 | const cssParser = (syntax: string, output: string) => { 29 | try { 30 | let isExist = fs.existsSync(output); 31 | let outdir = path.dirname(output); 32 | 33 | if (!isExist) { 34 | try { 35 | fs.writeFileSync(output, syntax); 36 | } catch(e) { 37 | Message.success(`generate folder at ${ output }`); 38 | fs.mkdirSync( outdir ); 39 | } finally { 40 | executeParser({ 41 | syntax: syntax, 42 | output: output, 43 | outdir: outdir 44 | }); 45 | } 46 | } 47 | 48 | } catch(e) { 49 | Message.danger(` 50 | Error Occurred ! 51 | message ; ${ Message.generate(e.message, "#FFFFFF") } 52 | please report this error at ${ pkg.bugs } 53 | `); 54 | } 55 | } 56 | 57 | export = cssParser; -------------------------------------------------------------------------------- /src/core/node.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import fs from "fs"; 3 | import { Command } from "commander"; 4 | import { pkg } from "../init"; 5 | import watcher from "../utils/watcher"; 6 | import Compiler from "../lib/Compiler"; 7 | import message from "../lib/Message"; 8 | 9 | /* initilize */ 10 | const program = new Command(); 11 | const pwd = process.cwd(); 12 | const config = globalThis.config; 13 | 14 | /* program information */ 15 | program 16 | .name(pkg.name) 17 | .usage(" [options]") 18 | .argument("", "command to execute") 19 | .version(pkg.version, "-v, --version", "show app version") 20 | .showSuggestionAfterError() 21 | 22 | program 23 | .command("init") 24 | .usage("") 25 | .description("initialize fisay configuration file") 26 | .action(() => { 27 | let dest: string = path.join(pwd, `/${pkg.name}.config.json`); 28 | let content: string = JSON.stringify(config, null, 2); 29 | fs.writeFileSync(dest, content); 30 | message.warning(`file config added at ./${path.basename(dest)}`); 31 | }); 32 | 33 | program 34 | .command("compile") 35 | .usage("[options]") 36 | .description("read file to compile class selector into css") 37 | .option("-i, --input ", "path file/folder input to compile") 38 | .option("-o, --output ", "path file result output") 39 | .option("-w, --watch [watch]", "path file/folder for watched") 40 | .action((options) => { 41 | if (!options.input && !config.input) message.danger("need options input [-i, --input]"); 42 | if (!options.output && !config.output) message.danger("need options input [-o, --output]"); 43 | 44 | let input = options.input ? path.join(pwd, options.input) : path.join(pwd, config.input); 45 | let output = options.output ? path.join(pwd, options.output) : path.join(pwd, config.output); 46 | let isWatched = options.watch ? true : config.watch; 47 | 48 | const compiler = new Compiler(input, output); 49 | if (isWatched) watcher(input, () => compiler.run()); 50 | else compiler.run(); 51 | 52 | }); 53 | 54 | 55 | export = program; -------------------------------------------------------------------------------- /src/constants/standardSize.ts: -------------------------------------------------------------------------------- 1 | import { getPrimeNumbers, getFractions, getIterateNumbers, ArrayToObject } from "../utils/functions"; 2 | 3 | export const BORDER_PREFIX_VALUES = { 4 | xs: "3px", 5 | sm: "5px", 6 | md: "8px", 7 | lg: "10px", 8 | xl: "12px", 9 | xxl: "14px", 10 | full: "9999px" 11 | } 12 | 13 | export const FLEX_PREFIX_VALUES = { 14 | "1": "1 1 0%", 15 | auto: "1 1 auto", 16 | initial: "0 1 auto", 17 | none: "none" 18 | } 19 | 20 | export const WAY_PRERIX_VALUES = { 21 | b: "bottom", 22 | t: "top", 23 | r: "right", 24 | l: "left" 25 | } 26 | 27 | export const FLEX_DIRECTION_PREFIX_VALUES = { 28 | row: "row", 29 | col: "column", 30 | "row-reverse": "row-reverse", 31 | "col-reverse": "column-reverse", 32 | } 33 | 34 | export const FLEX_WRAP_PREFIX_VALUES = { 35 | ...ArrayToObject(["wrap", "nowrap", "wrap-reverse"]) 36 | } 37 | 38 | export const PREFIX_VALUES = getPrimeNumbers(100); 39 | 40 | export const ASPECT_PREFIX_VALUES = { 41 | video: "16 / 9", 42 | square: "1 / 1", 43 | auto: "auto" 44 | } 45 | 46 | export const COLUMNS_PREFIX_VALUES = { 47 | ...ArrayToObject(getPrimeNumbers(13, 1)), /* 1 - 12 */ 48 | "auto": "auto", 49 | "3xs": "16rem", 50 | "2xs": "18rem", 51 | "xs": "20rem", 52 | "sm": "24rem", 53 | "md": "28rem", 54 | "lg": "32rem", 55 | "xl": "36rem", 56 | "2xl": "42rem", 57 | "3xl": "48rem", 58 | "4xl": "56rem", 59 | "5xl": "64rem", 60 | "6xl": "72rem", 61 | "7xl": "80rem", 62 | } 63 | 64 | export const FLEX_BASIS_PREFIX_VALUES = { 65 | ...ArrayToObject(PREFIX_VALUES), 66 | ...ArrayToObject(getIterateNumbers(0.5, 4)), 67 | ...getFractions(2), 68 | ...getFractions(3), 69 | ...getFractions(4), 70 | ...getFractions(5), 71 | ...getFractions(6), 72 | ...getFractions(12), 73 | "full": "100%", 74 | "wuto": "auto", 75 | } 76 | 77 | export const BREAK_PREFIX_VALUES = { 78 | ...ArrayToObject(["auto", "avoid", "all", "avoid-page", "page", "left", "right", "column"]) 79 | } 80 | 81 | export const BREAK_DECORATION_PREFIX_VALUES = { 82 | ...ArrayToObject(["clone", "slice"]) 83 | } 84 | 85 | export const Z_INDEX_VALUES = getPrimeNumbers(100, 10); 86 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fisay", 3 | "version": "0.2.5-development", 4 | "description": "a css framework like tailwind css", 5 | "main": "build/src/index.js", 6 | "private": false, 7 | "repository": "https://github.com/fiandev/fisay.git", 8 | "bugs": "https://github.com/fiandev/fisay/issues", 9 | "homepage": "https://github.com/fiandev/fisay#readme", 10 | "scripts": { 11 | "build": "rm -rf ./build && tsc --build && echo 'build success :)'", 12 | "test": "fisay compile -i ./__test__/ -o ./__test__/css/out.css", 13 | "test-watcher": "fisay compile -i ./__test__/ -o ./__test__/css/out.css -w", 14 | "dev": "./node_modules/.bin/nodemon", 15 | "bundle-min-css": "./node_modules/.bin/sass ./sass/components.scss:./dist/css/fisay.bundle.min.css -s 'compressed' --no-source-map -w", 16 | "bundle-css": "./node_modules/.bin/sass ./sass/components.scss:./dist/css/fisay.bundle.css -s 'expanded' --no-source-map -w" 17 | }, 18 | "bin": { 19 | "fisay": "./bin/fisay" 20 | }, 21 | "directories": { 22 | "src": "src" 23 | }, 24 | "keywords": [ 25 | "css", 26 | "framework", 27 | "js", 28 | "nodejs", 29 | "saas", 30 | "scss", 31 | "fisay" 32 | ], 33 | "author": "fiandev", 34 | "license": "MIT", 35 | "dependencies": { 36 | "autoprefixer": "^10.4.13", 37 | "chalk": "4.1.2", 38 | "cheerio": "^1.0.0-rc.12", 39 | "clear": "^0.1.0", 40 | "commander": "^9.4.1", 41 | "fs-extra": "^11.1.0", 42 | "fs-readdir-recursive": "^1.1.0", 43 | "node-watch": "^0.7.3", 44 | "postcss": "^8.4.21", 45 | "require-all": "^3.0.0", 46 | "sass": "^1.57.1" 47 | }, 48 | "devDependencies": { 49 | "@types/autoprefixer": "^10.2.0", 50 | "@types/node": "^18.11.9", 51 | "@types/require-all": "^3.0.3", 52 | "nodemon": "^2.0.20", 53 | "typescript": "^4.9.4" 54 | }, 55 | "browserslist": { 56 | "production": [ 57 | ">0.2%", 58 | "not dead", 59 | "not op_mini all", 60 | "last 100 versions" 61 | ], 62 | "development": [ 63 | "last 1 chrome version", 64 | "last 1 firefox version", 65 | "last 1 safari version", 66 | "last 1 ie version" 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/lib/Compiler.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import cheerio from "cheerio"; 3 | import fs from "fs"; 4 | import fsx from "fs-extra"; 5 | import readdir from "fs-readdir-recursive"; 6 | import Memories from "./Memories"; 7 | import Message from "./Message"; 8 | import propertiesParser from "../utils/propertiesParser"; 9 | import breakpointParser from "../utils/breakpointParser"; 10 | import cssParser from "../utils/cssParser"; 11 | import { scssBlob } from "../init"; 12 | 13 | export default class { 14 | private input: string; 15 | private output: string; 16 | private __output__: string; 17 | private config: any; 18 | 19 | public constructor (input: string, output: string) { 20 | /* initialize attributes */ 21 | this.output = output; 22 | this.input = input; 23 | 24 | // resolving path output 25 | this.__output__ = path.resolve(this.output); 26 | this.config = globalThis.config; 27 | } 28 | 29 | public async run () { 30 | /* set to default value */ 31 | if (globalThis["memory"]) delete globalThis["memory"]; 32 | globalThis["blob"] = scssBlob; 33 | 34 | /* check if target input is directory */ 35 | let isDir = fs.lstatSync(this.input).isDirectory(); 36 | if (isDir) await this.parseDirectory(this.input); 37 | else await this.compile(this.input); 38 | 39 | const memories = globalThis.memory; 40 | // normal breakpoint 41 | memories["normal"]?.map(v => propertiesParser(v)); 42 | 43 | // breakpoint 44 | for (let breakpoint in memories) { 45 | if (breakpoint !== "normal") await breakpointParser(breakpoint, memories[breakpoint]); 46 | } 47 | 48 | let blob = globalThis.blob; 49 | blob.replace(/(undefined)/g, ""); 50 | 51 | const blobCSS = blob; 52 | cssParser(blobCSS, this.__output__); 53 | 54 | Message.warning(`task completed!`); 55 | Message.success(`success compile ${ isDir ? "directory" : "file" } at ${ path.basename(this.input) }`); 56 | } 57 | 58 | private async parseDirectory (input: string) { 59 | const files = readdir(input); 60 | 61 | files.forEach(file => { 62 | let pathfile = path.join(this.input, `/${ file }`); 63 | this.compile(pathfile); 64 | }); 65 | } 66 | 67 | private async compile (input: string) { 68 | // get extension of file input 69 | let extension = path.extname(path.resolve(input)).slice(1); 70 | 71 | // ignore file when extension now allowed 72 | if ( !this.config.allowedExtension.includes(extension) ) return; 73 | 74 | const blob = fs.readFileSync(path.resolve(input)); 75 | const $ = cheerio.load(blob); 76 | 77 | // clear result files 78 | let isExist = fs.existsSync(this.__output__); 79 | if (isExist) fs.unlinkSync(this.__output__); 80 | 81 | // global memory 82 | globalThis.memory = Object.keys(typeof globalThis.memory === "undefined" ? {} : globalThis.memory).length !== 0 ? globalThis.memory : {}; 83 | 84 | // parsing body 85 | $("body, body *").each(function(i, e){ 86 | let element = $(e); 87 | let attrClass = element?.attr("class"); 88 | if (typeof attrClass !== "undefined") new Memories(attrClass); 89 | }); 90 | 91 | } 92 | 93 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /sass/includes/reset.scss: -------------------------------------------------------------------------------- 1 | *, 2 | ::before, 3 | ::after 4 | { 5 | box-sizing: border-box; 6 | /* 1 */ 7 | border-width: 0; 8 | /* 2 */ 9 | border-style: solid; 10 | /* 2 */ 11 | border-color: #e5e7eb; 12 | /* 2 */ 13 | } 14 | 15 | /* 16 | 1. Use a consistent sensible line-height in all browsers. 17 | 2. Prevent adjustments of font size after orientation changes in iOS. 18 | 3. Use a more readable tab size. 19 | 4. Use the user's configured `sans` font-family by default. 20 | 5. Use the user's configured `sans` font-feature-settings by default. 21 | */ 22 | 23 | html { 24 | line-height: 1.5; 25 | /* 1 */ 26 | -webkit-text-size-adjust: 100%; 27 | /* 2 */ 28 | -moz-tab-size: 4; 29 | /* 3 */ 30 | -o-tab-size: 4; 31 | tab-size: 4; 32 | /* 3 */ 33 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 34 | /* 4 */ 35 | font-feature-settings: normal; 36 | /* 5 */ 37 | } 38 | 39 | /* 40 | 1. Remove the margin in all browsers. 41 | 2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. 42 | */ 43 | 44 | body { 45 | margin: 0; 46 | /* 1 */ 47 | line-height: inherit; 48 | /* 2 */ 49 | } 50 | 51 | /* 52 | 1. Add the correct height in Firefox. 53 | 2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) 54 | 3. Ensure horizontal rules are visible by default. 55 | */ 56 | 57 | hr { 58 | height: 0; 59 | /* 1 */ 60 | color: inherit; 61 | /* 2 */ 62 | border-top-width: 1px; 63 | /* 3 */ 64 | } 65 | 66 | /* 67 | Add the correct text decoration in Chrome, Edge, and Safari. 68 | */ 69 | 70 | abbr:where([title]) { 71 | -webkit-text-decoration: underline dotted; 72 | text-decoration: underline dotted; 73 | } 74 | 75 | /* 76 | Remove the default font size and weight for headings. 77 | */ 78 | 79 | h1, 80 | h2, 81 | h3, 82 | h4, 83 | h5, 84 | h6 { 85 | font-size: inherit; 86 | font-weight: inherit; 87 | } 88 | 89 | /* 90 | Reset links to optimize for opt-in styling instead of opt-out. 91 | */ 92 | 93 | a { 94 | color: inherit; 95 | text-decoration: inherit; 96 | } 97 | 98 | /* 99 | Add the correct font weight in Edge and Safari. 100 | */ 101 | 102 | b, 103 | strong { 104 | font-weight: bolder; 105 | } 106 | 107 | /* 108 | 1. Use the user's configured `mono` font family by default. 109 | 2. Correct the odd `em` font sizing in all browsers. 110 | */ 111 | 112 | code, 113 | kbd, 114 | samp, 115 | pre { 116 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 117 | /* 1 */ 118 | font-size: 1em; 119 | /* 2 */ 120 | } 121 | 122 | /* 123 | Add the correct font size in all browsers. 124 | */ 125 | 126 | small { 127 | font-size: 80%; 128 | } 129 | 130 | /* 131 | Prevent `sub` and `sup` elements from affecting the line height in all browsers. 132 | */ 133 | 134 | sub, 135 | sup { 136 | font-size: 75%; 137 | line-height: 0; 138 | position: relative; 139 | vertical-align: baseline; 140 | } 141 | 142 | sub { 143 | bottom: -0.25em; 144 | } 145 | 146 | sup { 147 | top: -0.5em; 148 | } 149 | 150 | /* 151 | 1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) 152 | 2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) 153 | 3. Remove gaps between table borders by default. 154 | */ 155 | 156 | table { 157 | text-indent: 0; 158 | /* 1 */ 159 | border-color: inherit; 160 | /* 2 */ 161 | border-collapse: collapse; 162 | /* 3 */ 163 | } 164 | 165 | /* 166 | 1. Change the font styles in all browsers. 167 | 2. Remove the margin in Firefox and Safari. 168 | 3. Remove default padding in all browsers. 169 | */ 170 | 171 | button, 172 | input, 173 | optgroup, 174 | select, 175 | textarea { 176 | font-family: inherit; 177 | /* 1 */ 178 | font-size: 100%; 179 | /* 1 */ 180 | font-weight: inherit; 181 | /* 1 */ 182 | line-height: inherit; 183 | /* 1 */ 184 | color: inherit; 185 | /* 1 */ 186 | margin: 0; 187 | /* 2 */ 188 | padding: 0; 189 | /* 3 */ 190 | } 191 | 192 | /* 193 | Remove the inheritance of text transform in Edge and Firefox. 194 | */ 195 | 196 | button, 197 | select { 198 | text-transform: none; 199 | } 200 | 201 | /* 202 | 1. Correct the inability to style clickable types in iOS and Safari. 203 | 2. Remove default button styles. 204 | */ 205 | 206 | button, 207 | [type='button'], 208 | [type='reset'], 209 | [type='submit'] { 210 | -webkit-appearance: button; 211 | /* 1 */ 212 | background-color: transparent; 213 | /* 2 */ 214 | background-image: none; 215 | /* 2 */ 216 | } 217 | 218 | /* 219 | Use the modern Firefox focus style for all focusable elements. 220 | */ 221 | 222 | :-moz-focusring { 223 | outline: auto; 224 | } 225 | 226 | /* 227 | Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) 228 | */ 229 | 230 | :-moz-ui-invalid { 231 | box-shadow: none; 232 | } 233 | 234 | /* 235 | Add the correct vertical alignment in Chrome and Firefox. 236 | */ 237 | 238 | progress { 239 | vertical-align: baseline; 240 | } 241 | 242 | /* 243 | Correct the cursor style of increment and decrement buttons in Safari. 244 | */ 245 | 246 | ::-webkit-inner-spin-button, 247 | ::-webkit-outer-spin-button { 248 | height: auto; 249 | } 250 | 251 | /* 252 | 1. Correct the odd appearance in Chrome and Safari. 253 | 2. Correct the outline style in Safari. 254 | */ 255 | 256 | [type='search'] { 257 | -webkit-appearance: textfield; 258 | /* 1 */ 259 | outline-offset: -2px; 260 | /* 2 */ 261 | } 262 | 263 | /* 264 | Remove the inner padding in Chrome and Safari on macOS. 265 | */ 266 | 267 | ::-webkit-search-decoration { 268 | -webkit-appearance: none; 269 | } 270 | 271 | /* 272 | 1. Correct the inability to style clickable types in iOS and Safari. 273 | 2. Change font properties to `inherit` in Safari. 274 | */ 275 | 276 | ::-webkit-file-upload-button { 277 | -webkit-appearance: button; 278 | /* 1 */ 279 | font: inherit; 280 | /* 2 */ 281 | } 282 | 283 | /* 284 | Add the correct display in Chrome and Safari. 285 | */ 286 | 287 | summary { 288 | display: list-item; 289 | } 290 | 291 | /* 292 | Removes the default spacing and border for appropriate elements. 293 | */ 294 | 295 | blockquote, 296 | dl, 297 | dd, 298 | h1, 299 | h2, 300 | h3, 301 | h4, 302 | h5, 303 | h6, 304 | hr, 305 | figure, 306 | p, 307 | pre { 308 | margin: 0; 309 | } 310 | 311 | fieldset { 312 | margin: 0; 313 | padding: 0; 314 | } 315 | 316 | legend { 317 | padding: 0; 318 | } 319 | 320 | ol, 321 | ul, 322 | menu { 323 | list-style: none; 324 | margin: 0; 325 | padding: 0; 326 | } 327 | 328 | /* 329 | Prevent resizing textareas horizontally by default. 330 | */ 331 | 332 | textarea { 333 | resize: vertical; 334 | } 335 | 336 | /* 337 | 1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) 338 | 2. Set the default placeholder color to the user's configured gray 400 color. 339 | */ 340 | 341 | input::-moz-placeholder, textarea::-moz-placeholder { 342 | opacity: 1; 343 | /* 1 */ 344 | color: #9ca3af; 345 | /* 2 */ 346 | } 347 | 348 | input::placeholder, 349 | textarea::placeholder { 350 | opacity: 1; 351 | /* 1 */ 352 | color: #9ca3af; 353 | /* 2 */ 354 | } 355 | 356 | /* 357 | Set the default cursor for buttons. 358 | */ 359 | 360 | button, 361 | [role="button"] { 362 | cursor: pointer; 363 | } 364 | 365 | /* 366 | Make sure disabled buttons don't get the pointer cursor. 367 | */ 368 | 369 | :disabled { 370 | cursor: default; 371 | } 372 | 373 | /* 374 | 1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) 375 | 2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) 376 | This can trigger a poorly considered lint error in some tools but is included by design. 377 | */ 378 | 379 | img, 380 | svg, 381 | video, 382 | canvas, 383 | audio, 384 | iframe, 385 | embed, 386 | object { 387 | display: block; 388 | /* 1 */ 389 | vertical-align: middle; 390 | /* 2 */ 391 | } 392 | 393 | /* 394 | Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) 395 | */ 396 | 397 | img, 398 | video { 399 | max-width: 100%; 400 | height: auto; 401 | } 402 | 403 | /* Make elements with the HTML hidden attribute stay hidden by default */ 404 | 405 | [hidden] { 406 | display: none; 407 | } -------------------------------------------------------------------------------- /__test__/css/out.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "sourceRoot": "", 4 | "sources": [ 5 | "data:;charset=utf-8,@mixin%20devices%20($breakpoint)%20%7B%20@if%20$breakpoint%20==%20verySmall%20%7B%20@media%20only%20screen%20and%20(min-width:%200px)%20%7B%20@content;%20%7D%20%7D%20@if%20$breakpoint%20==%20small%20%7B%20@media%20only%20screen%20and%20(max-width:%20575px)%20%7B%20@content;%20%7D%20%7D%20@if%20$breakpoint%20==%20medium%20%7B%20@media%20only%20screen%20and%20(min-width:%20576px)%20%7B%20@content;%20%7D%20%7D%20@if%20$breakpoint%20==%20large%20%7B%20@media%20only%20screen%20and%20(min-width:%20992px)%20%7B%20@content;%20%7D%20%7D%20@if%20$breakpoint%20==%20veryLarge%20%7B%20@media%20only%20screen%20and%20(min-width:%201200px)%20%7B%20@content;%20%7D%20%7D%20@if%20$breakpoint%20==%20superLarge%20%7B%20@media%20only%20screen%20and%20(min-width:%201400px)%20%7B%20@content;%20%7D%20%7D%7D*,::before,::after%20%7B%20box-sizing:%20border-box;%20border-width:%200;%20border-style:%20solid;%20border-color:%20%23e5e7eb;%20%7D/*1.%20Use%20a%20consistent%20sensible%20line-height%20in%20all%20browsers.2.%20Prevent%20adjustments%20of%20font%20size%20after%20orientation%20changes%20in%20iOS.3.%20Use%20a%20more%20readable%20tab%20size.4.%20Use%20the%20user's%20configured%20%60sans%60%20font-family%20by%20default.5.%20Use%20the%20user's%20configured%20%60sans%60%20font-feature-settings%20by%20default.*/html%20%7B%20line-height:%201.5;%20-webkit-text-size-adjust:%20100%25;%20-moz-tab-size:%204;%20-o-tab-size:%204;%20tab-size:%204;%20font-family:%20ui-sans-serif,%20system-ui,%20-apple-system,%20BlinkMacSystemFont,%20%22Segoe%20UI%22,%20Roboto,%20%22Helvetica%20Neue%22,%20Arial,%20%22Noto%20Sans%22,%20sans-serif,%20%22Apple%20Color%20Emoji%22,%20%22Segoe%20UI%20Emoji%22,%20%22Segoe%20UI%20Symbol%22,%20%22Noto%20Color%20Emoji%22;%20font-feature-settings:%20normal;%20%7D/*1.%20Remove%20the%20margin%20in%20all%20browsers.2.%20Inherit%20line-height%20from%20%60html%60%20so%20users%20can%20set%20them%20as%20a%20class%20directly%20on%20the%20%60html%60%20element.*/body%20%7B%20margin:%200;%20line-height:%20inherit;%20%7D/*1.%20Add%20the%20correct%20height%20in%20Firefox.2.%20Correct%20the%20inheritance%20of%20border%20color%20in%20Firefox.%20(https:show_bug.cgi?id=190655)3.%20Ensure%20horizontal%20rules%20are%20visible%20by%20default.*/hr%20%7B%20height:%200;%20color:%20inherit;%20border-top-width:%201px;%20%7D/*Add%20the%20correct%20text%20decoration%20in%20Chrome,%20Edge,%20and%20Safari.*/abbr:where(%5Btitle%5D)%20%7B%20-webkit-text-decoration:%20underline%20dotted;%20text-decoration:%20underline%20dotted;%7D/*Remove%20the%20default%20font%20size%20and%20weight%20for%20headings.*/h1,h2,h3,h4,h5,h6%20%7B%20font-size:%20inherit;%20font-weight:%20inherit;%7D/*Reset%20links%20to%20optimize%20for%20opt-in%20styling%20instead%20of%20opt-out.*/a%20%7B%20color:%20inherit;%20text-decoration:%20inherit;%7D/*Add%20the%20correct%20font%20weight%20in%20Edge%20and%20Safari.*/b,strong%20%7B%20font-weight:%20bolder;%7D/*1.%20Use%20the%20user's%20configured%20%60mono%60%20font%20family%20by%20default.2.%20Correct%20the%20odd%20%60em%60%20font%20sizing%20in%20all%20browsers.*/code,kbd,samp,pre%20%7B%20font-family:%20ui-monospace,%20SFMono-Regular,%20Menlo,%20Monaco,%20Consolas,%20%22Liberation%20Mono%22,%20%22Courier%20New%22,%20monospace;%20font-size:%201em;%20%7D/*Add%20the%20correct%20font%20size%20in%20all%20browsers.*/small%20%7B%20font-size:%2080%25;%7D/*Prevent%20%60sub%60%20and%20%60sup%60%20elements%20from%20affecting%20the%20line%20height%20in%20all%20browsers.*/sub,sup%20%7B%20font-size:%2075%25;%20line-height:%200;%20position:%20relative;%20vertical-align:%20baseline;%7Dsub%20%7B%20bottom:%20-0.25em;%7Dsup%20%7B%20top:%20-0.5em;%7D/*1.%20Remove%20text%20indentation%20from%20table%20contents%20in%20Chrome%20and%20Safari.%20(https:show_bug.cgi?id=201297)2.%20Correct%20table%20border%20color%20inheritance%20in%20all%20Chrome%20and%20Safari.%20(https:show_bug.cgi?id=195016)3.%20Remove%20gaps%20between%20table%20borders%20by%20default.*/table%20%7B%20text-indent:%200;%20border-color:%20inherit;%20border-collapse:%20collapse;%20%7D/*1.%20Change%20the%20font%20styles%20in%20all%20browsers.2.%20Remove%20the%20margin%20in%20Firefox%20and%20Safari.3.%20Remove%20default%20padding%20in%20all%20browsers.*/button,input,optgroup,select,textarea%20%7B%20font-family:%20inherit;%20font-size:%20100%25;%20font-weight:%20inherit;%20line-height:%20inherit;%20color:%20inherit;%20margin:%200;%20padding:%200;%20%7D/*Remove%20the%20inheritance%20of%20text%20transform%20in%20Edge%20and%20Firefox.*/button,select%20%7B%20text-transform:%20none;%7D/*1.%20Correct%20the%20inability%20to%20style%20clickable%20types%20in%20iOS%20and%20Safari.2.%20Remove%20default%20button%20styles.*/button,%5Btype='button'%5D,%5Btype='reset'%5D,%5Btype='submit'%5D%20%7B%20-webkit-appearance:%20button;%20background-color:%20transparent;%20background-image:%20none;%20%7D/*Use%20the%20modern%20Firefox%20focus%20style%20for%20all%20focusable%20elements.*/:-moz-focusring%20%7B%20outline:%20auto;%7D/*Remove%20the%20additional%20%60:invalid%60%20styles%20in%20Firefox.%20(https:forms.css%23L728-L737)*/:-moz-ui-invalid%20%7B%20box-shadow:%20none;%7D/*Add%20the%20correct%20vertical%20alignment%20in%20Chrome%20and%20Firefox.*/progress%20%7B%20vertical-align:%20baseline;%7D/*Correct%20the%20cursor%20style%20of%20increment%20and%20decrement%20buttons%20in%20Safari.*/::-webkit-inner-spin-button,::-webkit-outer-spin-button%20%7B%20height:%20auto;%7D/*1.%20Correct%20the%20odd%20appearance%20in%20Chrome%20and%20Safari.2.%20Correct%20the%20outline%20style%20in%20Safari.*/%5Btype='search'%5D%20%7B%20-webkit-appearance:%20textfield;%20outline-offset:%20-2px;%20%7D/*Remove%20the%20inner%20padding%20in%20Chrome%20and%20Safari%20on%20macOS.*/::-webkit-search-decoration%20%7B%20-webkit-appearance:%20none;%7D/*1.%20Correct%20the%20inability%20to%20style%20clickable%20types%20in%20iOS%20and%20Safari.2.%20Change%20font%20properties%20to%20%60inherit%60%20in%20Safari.*/::-webkit-file-upload-button%20%7B%20-webkit-appearance:%20button;%20font:%20inherit;%20%7D/*Add%20the%20correct%20display%20in%20Chrome%20and%20Safari.*/summary%20%7B%20display:%20list-item;%7D/*Removes%20the%20default%20spacing%20and%20border%20for%20appropriate%20elements.*/blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre%20%7B%20margin:%200;%7Dfieldset%20%7B%20margin:%200;%20padding:%200;%7Dlegend%20%7B%20padding:%200;%7Dol,ul,menu%20%7B%20list-style:%20none;%20margin:%200;%20padding:%200;%7D/*Prevent%20resizing%20textareas%20horizontally%20by%20default.*/textarea%20%7B%20resize:%20vertical;%7D/*1.%20Reset%20the%20default%20placeholder%20opacity%20in%20Firefox.%20(https:3300)2.%20Set%20the%20default%20placeholder%20color%20to%20the%20user's%20configured%20gray%20400%20color.*/input::-moz-placeholder,%20textarea::-moz-placeholder%20%7B%20opacity:%201;%20color:%20%239ca3af;%20%7Dinput::placeholder,textarea::placeholder%20%7B%20opacity:%201;%20color:%20%239ca3af;%20%7D/*Set%20the%20default%20cursor%20for%20buttons.*/button,%5Brole=%22button%22%5D%20%7B%20cursor:%20pointer;%7D/*Make%20sure%20disabled%20buttons%20don't%20get%20the%20pointer%20cursor.*/:disabled%20%7B%20cursor:%20default;%7D/*1.%20Make%20replaced%20elements%20%60display:%20block%60%20by%20default.%20(https:14)2.%20Add%20%60vertical-align:%20middle%60%20to%20align%20replaced%20elements%20more%20sensibly%20by%20default.%20(https:14%23issuecomment-634934210)%20This%20can%20trigger%20a%20poorly%20considered%20lint%20error%20in%20some%20tools%20but%20is%20included%20by%20design.*/img,svg,video,canvas,audio,iframe,embed,object%20%7B%20display:%20block;%20vertical-align:%20middle;%20%7D/*Constrain%20images%20and%20videos%20to%20the%20parent%20width%20and%20preserve%20their%20intrinsic%20aspect%20ratio.%20(https:14)*/img,video%20%7B%20max-width:%20100%25;%20height:%20auto;%7D%5Bhidden%5D%20%7B%20display:%20none;%7D.grid%20%7Bdisplay:%20grid;%7D.flex-1%20%7Bflex:%201%201%200%25%7D.flex-auto%20%7Bflex:%201%201%20auto%7D.flex%20%7Bdisplay:%20flex;%7D.flex-wrap-reverse%20%7Bflex-wrap:%20wrap-reverse%7D.flex-col%20%7Bflex-direction:%20column%7D.p-8%20%7Bpadding:%201.60rem;%7D.m-0%20%7Bmargin:%200.00rem;%7D.items-center%20%7Balign-items:%20center;%7D.justify-center%20%7Bjustify-content:%20center;%7D.break-after-column%20%7Bbreak-after:%20column%7D.break-before-page%20%7Bbreak-before:%20page%7D.columns-1%20%7Bcolumns:%201%7D.aspect-video%20%7Baspect-ratio:%2016%20/%209%7D.z-20%20%7Bz-index:%2020;%7D.z-90%20%7Bz-index:%2090;%7D.gap-x-1%20%7Brow-gap:%200.20rem;%7D.items-start%20%7Balign-items:%20start;%7D.items-end%20%7Balign-items:%20end;%7D.grid-cols-6%20%7Bgrid-template-columns:%20repeat(6,%201fr);%7D.items-evenly%20%7Balign-items:%20evenly;%7D.p-2%20%7Bpadding:%200.40rem;%7D.rounded-md%20%7Bborder:%208px;%7D.rounded-lg%20%7Bborder:%2010px;%7D.basis-1%5C.5%20%7Bflex-basis:%201.5;%7D.px-10%20%7Bpadding:%200%202.00rem;%7D.py-12%20%7Bpadding:%202.40rem%200;%7D.basis-8%20%7Bflex-basis:%208;%7D.p-%5C%5B3px%5C%5D%20%7Bpadding:%203px;%7D.gap-x-%5C%5B10rem%5C%5D%20%7Brow-gap:%2010rem;%7D.gap-18%20%7Bgap:%203.60rem;%7D.gap-y-20%20%7Bcolumn-gap:%204.00rem;%7D.rounded-full%20%7Bborder:%209999px;%7D@include%20devices(medium)%20%7B.md%5C:flex-none%20%7Bflex:%20none%7D.md%5C:flex-nowrap%20%7Bflex-wrap:%20nowrap%7D.md%5C:flex-row-reverse%20%7Bflex-direction:%20row-reverse%7D.md%5C:gap-%5C%5B2rem%5C%5D%20%7Bgap:%202rem;%7D.md%5C:grid-cols-4%20%7Bgrid-template-columns:%20repeat(4,%201fr);%7D.md%5C:basis-0%5C.5%20%7Bflex-basis:%200.5;%7D.md%5C:basis-1%5C/12%20%7Bflex-basis:%208.333333333333332%25;%7D.md%5C:rounded-%5C%5B5px%5C%5D%20%7Bborder:%205px;%7D%7D@include%20devices(large)%20%7B.lg%5C:flex-initial%20%7Bflex:%200%201%20auto%7D.lg%5C:flex-row%20%7Bflex-direction:%20row%7D.lg%5C:grid-cols-5%20%7Bgrid-template-columns:%20repeat(5,%201fr);%7D.lg%5C:basis-3%5C.5%20%7Bflex-basis:%203.5;%7D.lg%5C:mb-8%20%7Bmargin-bottom:%201.60rem;%7D.lg%5C:basis-1%5C/4%20%7Bflex-basis:%2025%25;%7D%7D@include%20devices(small)%20%7B.sm%5C:grid-cols-3%20%7Bgrid-template-columns:%20repeat(3,%201fr);%7D.sm%5C:gap-4%20%7Bgap:%200.80rem;%7D%7D" 6 | ], 7 | "names": [], 8 | "mappings": "AAA2iB;EAAqB;EAAwB;EAAiB;EAAqB;EAAwB;AAAkS;EAAO;EAAkB;EAAgC;EAAkB;EAAgB;EAAa;EAA8N;EAAgC;AAA4I;EAAO;EAAW;EAAuB;AAAgL;EAAK;EAAW;EAAgB;EAAwB;AAAgE;EAAsB;EAA2C;EAAmC;AAAyD;EAAoB;EAAoB;EAAsB;AAAkE;EAAI;EAAgB;EAA0B;AAAmD;EAAW;EAAqB;AAAmH;EAAoB;EAAiH;EAAiB;AAA8C;EAAQ;EAAgB;AAAoF;EAAU;EAAgB;EAAgB;EAAoB;;;AAA0B;EAAM;;;AAAiB;EAAM;EAAa;AAAyP;EAAQ;EAAgB;EAAuB;EAA4B;AAAmI;EAAwC;EAAsB;EAAiB;EAAsB;EAAsB;EAAgB;EAAW;EAAa;AAAiE;EAAgB;EAAsB;AAAwG;EAAwD;EAA4B;EAA+B;EAAyB;AAAkE;EAAkB;EAAe;AAAmF;EAAmB;EAAkB;AAA6D;EAAW;EAA0B;AAA0E;EAA0D;EAAc;AAA8F;EAAkB;EAA+B;EAAuB;AAA2D;EAA8B;EAA0B;AAAyH;EAA+B;EAA4B;EAAgB;AAAiD;EAAU;EAAoB;AAAoE;EAAqD;;;AAAW;EAAW;EAAW;;;AAAY;EAAS;;;AAAY;EAAa;EAAkB;EAAW;EAAY;AAAuD;EAAW;EAAkB;AAAkJ;EAAsD;EAAY;;;AAAiB;EAA2C;EAAY;EAAiB;AAAuC;EAAyB;EAAiB;AAA4D;EAAY;EAAiB;AAAoR;EAAiD;EAAgB;EAAyB;AAAyG;EAAY;EAAiB;;;AAAc;EAAW;;;AAAe;EAAO;;;AAAe;EAAS;;;AAAa;EAAY;;;AAAe;EAAO;;;AAAe;EAAoB;;;AAAwB;EAAW;;;AAAuB;EAAM;;;AAAkB;EAAM;;;AAAiB;EAAe;;;AAAqB;EAAiB;;;AAAyB;EAAqB;;;AAAoB;EAAoB;;;AAAmB;EAAY;;;AAAW;EAAe;;;AAAqB;EAAO;;;AAAa;EAAO;;;AAAa;EAAU;;;AAAkB;EAAc;;;AAAoB;EAAY;;;AAAkB;EAAc;;;AAAuC;EAAe;;;AAAqB;EAAM;;;AAAkB;EAAa;;;AAAa;EAAa;;;AAAc;EAAa;;;AAAiB;EAAQ;;;AAAoB;EAAQ;;;AAAoB;EAAU;;;AAAe;EAAY;;;AAAc;EAAkB;;;AAAgB;EAAS;;;AAAc;EAAW;;;AAAqB;EAAe;;;AAA7pN;EAAusN;IAAgB;;EAAW;IAAkB;;EAAkB;IAAuB;;EAA4B;IAAmB;;EAAW;IAAkB;;EAAuC;IAAiB;;EAAiB;IAAkB;;EAAgC;IAAsB;;;AAAr6N;EAA48N;IAAmB;;EAAe;IAAe;;EAAoB;IAAkB;;EAAuC;IAAiB;;EAAiB;IAAW;;EAAwB;IAAiB;;;AAA30O;EAAs3O;IAAkB;;EAAuC;IAAY" 9 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "./src/**/*.d.ts", 4 | "./src/**/*.ts", 5 | "./src/**/*.scss" 6 | ], 7 | "exclude": ["node_modules"], 8 | "compilerOptions": { 9 | /* Visit https://aka.ms/tsconfig to read more about this file */ 10 | 11 | /* Projects */ 12 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 13 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 14 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 15 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 16 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 17 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 18 | 19 | /* Language and Environment */ 20 | "target": "es2015", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 21 | // "lib": ["es15", "es6"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 22 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 23 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 24 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 25 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 26 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 27 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 28 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 29 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 30 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 31 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 32 | 33 | /* Modules */ 34 | "module": "commonjs", /* Specify what module code is generated. */ 35 | "rootDir": "./", /* Specify the root folder within your source files. */ 36 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 37 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 38 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 39 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 40 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 41 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 42 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 43 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 44 | "resolveJsonModule": true, /* Enable importing .json files. */ 45 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 46 | 47 | /* JavaScript Support */ 48 | "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 49 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 50 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 51 | 52 | /* Emit */ 53 | "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 54 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 55 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 56 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | "outDir": "./build", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 67 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 68 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 69 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 70 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 71 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 72 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 73 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 74 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 75 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 76 | 77 | /* Interop Constraints */ 78 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 83 | 84 | /* Type Checking */ 85 | // "strict": true, /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /__test__/css/out.css: -------------------------------------------------------------------------------- 1 | *, ::before, ::after { 2 | -webkit-box-sizing: border-box; 3 | -moz-box-sizing: border-box; 4 | box-sizing: border-box; 5 | border-width: 0; 6 | border-style: solid; 7 | border-color: #e5e7eb; 8 | } /*1. Use a consistent sensible line-height in all browsers.2. Prevent adjustments of font size after orientation changes in iOS.3. Use a more readable tab size.4. Use the user's configured `sans` font-family by default.5. Use the user's configured `sans` font-feature-settings by default.*/ 9 | html { 10 | line-height: 1.5; 11 | -webkit-text-size-adjust: 100%; 12 | -moz-tab-size: 4; 13 | -o-tab-size: 4; 14 | tab-size: 4; 15 | font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 16 | -webkit-font-feature-settings: normal; 17 | -moz-font-feature-settings: normal; 18 | font-feature-settings: normal; 19 | } /*1. Remove the margin in all browsers.2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.*/ 20 | body { 21 | margin: 0; 22 | line-height: inherit; 23 | } /*1. Add the correct height in Firefox.2. Correct the inheritance of border color in Firefox. (https:show_bug.cgi?id=190655)3. Ensure horizontal rules are visible by default.*/ 24 | hr { 25 | height: 0; 26 | color: inherit; 27 | border-top-width: 1px; 28 | } /*Add the correct text decoration in Chrome, Edge, and Safari.*/ 29 | abbr:where([title]) { 30 | -webkit-text-decoration: underline dotted; 31 | text-decoration: underline dotted; 32 | } /*Remove the default font size and weight for headings.*/ 33 | h1, h2, h3, h4, h5, h6 { 34 | font-size: inherit; 35 | font-weight: inherit; 36 | } /*Reset links to optimize for opt-in styling instead of opt-out.*/ 37 | a { 38 | color: inherit; 39 | text-decoration: inherit; 40 | } /*Add the correct font weight in Edge and Safari.*/ 41 | b, strong { 42 | font-weight: bolder; 43 | } /*1. Use the user's configured `mono` font family by default.2. Correct the odd `em` font sizing in all browsers.*/ 44 | code, kbd, samp, pre { 45 | font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 46 | font-size: 1em; 47 | } /*Add the correct font size in all browsers.*/ 48 | small { 49 | font-size: 80%; 50 | } /*Prevent `sub` and `sup` elements from affecting the line height in all browsers.*/ 51 | sub, sup { 52 | font-size: 75%; 53 | line-height: 0; 54 | position: relative; 55 | vertical-align: baseline; 56 | } 57 | 58 | sub { 59 | bottom: -0.25em; 60 | } 61 | 62 | sup { 63 | top: -0.5em; 64 | } /*1. Remove text indentation from table contents in Chrome and Safari. (https:show_bug.cgi?id=201297)2. Correct table border color inheritance in all Chrome and Safari. (https:show_bug.cgi?id=195016)3. Remove gaps between table borders by default.*/ 65 | table { 66 | text-indent: 0; 67 | border-color: inherit; 68 | border-collapse: collapse; 69 | } /*1. Change the font styles in all browsers.2. Remove the margin in Firefox and Safari.3. Remove default padding in all browsers.*/ 70 | button, input, optgroup, select, textarea { 71 | font-family: inherit; 72 | font-size: 100%; 73 | font-weight: inherit; 74 | line-height: inherit; 75 | color: inherit; 76 | margin: 0; 77 | padding: 0; 78 | } /*Remove the inheritance of text transform in Edge and Firefox.*/ 79 | button, select { 80 | text-transform: none; 81 | } /*1. Correct the inability to style clickable types in iOS and Safari.2. Remove default button styles.*/ 82 | button, [type=button], [type=reset], [type=submit] { 83 | -webkit-appearance: button; 84 | background-color: transparent; 85 | background-image: none; 86 | } /*Use the modern Firefox focus style for all focusable elements.*/ 87 | :-moz-focusring { 88 | outline: auto; 89 | } /*Remove the additional `:invalid` styles in Firefox. (https:forms.css#L728-L737)*/ 90 | :-moz-ui-invalid { 91 | box-shadow: none; 92 | } /*Add the correct vertical alignment in Chrome and Firefox.*/ 93 | progress { 94 | vertical-align: baseline; 95 | } /*Correct the cursor style of increment and decrement buttons in Safari.*/ 96 | ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { 97 | height: auto; 98 | } /*1. Correct the odd appearance in Chrome and Safari.2. Correct the outline style in Safari.*/ 99 | [type=search] { 100 | -webkit-appearance: textfield; 101 | outline-offset: -2px; 102 | } /*Remove the inner padding in Chrome and Safari on macOS.*/ 103 | ::-webkit-search-decoration { 104 | -webkit-appearance: none; 105 | } /*1. Correct the inability to style clickable types in iOS and Safari.2. Change font properties to `inherit` in Safari.*/ 106 | ::-webkit-file-upload-button { 107 | -webkit-appearance: button; 108 | font: inherit; 109 | } /*Add the correct display in Chrome and Safari.*/ 110 | summary { 111 | display: list-item; 112 | } /*Removes the default spacing and border for appropriate elements.*/ 113 | blockquote, dl, dd, h1, h2, h3, h4, h5, h6, hr, figure, p, pre { 114 | margin: 0; 115 | } 116 | 117 | fieldset { 118 | margin: 0; 119 | padding: 0; 120 | } 121 | 122 | legend { 123 | padding: 0; 124 | } 125 | 126 | ol, ul, menu { 127 | list-style: none; 128 | margin: 0; 129 | padding: 0; 130 | } /*Prevent resizing textareas horizontally by default.*/ 131 | textarea { 132 | resize: vertical; 133 | } /*1. Reset the default placeholder opacity in Firefox. (https:3300)2. Set the default placeholder color to the user's configured gray 400 color.*/ 134 | input::-moz-placeholder, textarea::-moz-placeholder { 135 | opacity: 1; 136 | color: #9ca3af; 137 | } 138 | 139 | input::-webkit-input-placeholder, textarea::-webkit-input-placeholder { 140 | opacity: 1; 141 | color: #9ca3af; 142 | } 143 | 144 | input:-moz-placeholder, textarea:-moz-placeholder { 145 | opacity: 1; 146 | color: #9ca3af; 147 | } 148 | 149 | input:-ms-input-placeholder, textarea:-ms-input-placeholder { 150 | opacity: 1; 151 | color: #9ca3af; 152 | } 153 | 154 | input::-ms-input-placeholder, textarea::-ms-input-placeholder { 155 | opacity: 1; 156 | color: #9ca3af; 157 | } 158 | 159 | input::placeholder, textarea::placeholder { 160 | opacity: 1; 161 | color: #9ca3af; 162 | } /*Set the default cursor for buttons.*/ 163 | button, [role=button] { 164 | cursor: pointer; 165 | } /*Make sure disabled buttons don't get the pointer cursor.*/ 166 | :disabled { 167 | cursor: default; 168 | } /*1. Make replaced elements `display: block` by default. (https:14)2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https:14#issuecomment-634934210) This can trigger a poorly considered lint error in some tools but is included by design.*/ 169 | img, svg, video, canvas, audio, iframe, embed, object { 170 | display: block; 171 | vertical-align: middle; 172 | } /*Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https:14)*/ 173 | img, video { 174 | max-width: 100%; 175 | height: auto; 176 | } 177 | 178 | [hidden] { 179 | display: none; 180 | } 181 | 182 | .grid { 183 | display: grid; 184 | } 185 | 186 | .flex-1 { 187 | -webkit-box-flex: 1; 188 | -webkit-flex: 1 1 0%; 189 | -moz-box-flex: 1; 190 | -ms-flex: 1 1 0%; 191 | flex: 1 1 0%; 192 | } 193 | 194 | .flex-auto { 195 | -webkit-box-flex: 1; 196 | -webkit-flex: 1 1 auto; 197 | -moz-box-flex: 1; 198 | -ms-flex: 1 1 auto; 199 | flex: 1 1 auto; 200 | } 201 | 202 | .flex { 203 | display: -webkit-box; 204 | display: -webkit-flex; 205 | display: -moz-box; 206 | display: -ms-flexbox; 207 | display: flex; 208 | } 209 | 210 | .flex-wrap-reverse { 211 | -webkit-flex-wrap: wrap-reverse; 212 | -ms-flex-wrap: wrap-reverse; 213 | flex-wrap: wrap-reverse; 214 | } 215 | 216 | .flex-col { 217 | -webkit-box-orient: vertical; 218 | -webkit-box-direction: normal; 219 | -webkit-flex-direction: column; 220 | -moz-box-orient: vertical; 221 | -moz-box-direction: normal; 222 | -ms-flex-direction: column; 223 | flex-direction: column; 224 | } 225 | 226 | .p-8 { 227 | padding: 1.6rem; 228 | } 229 | 230 | .m-0 { 231 | margin: 0rem; 232 | } 233 | 234 | .items-center { 235 | -webkit-box-align: center; 236 | -webkit-align-items: center; 237 | -moz-box-align: center; 238 | -ms-flex-align: center; 239 | align-items: center; 240 | } 241 | 242 | .justify-center { 243 | -webkit-box-pack: center; 244 | -webkit-justify-content: center; 245 | -moz-box-pack: center; 246 | -ms-flex-pack: center; 247 | justify-content: center; 248 | } 249 | 250 | .break-after-column { 251 | -webkit-column-break-after: column; 252 | -moz-column-break-after: column; 253 | break-after: column; 254 | } 255 | 256 | .break-before-page { 257 | -webkit-column-break-before: page; 258 | -moz-column-break-before: page; 259 | break-before: page; 260 | } 261 | 262 | .columns-1 { 263 | -webkit-columns: 1; 264 | -moz-columns: 1; 265 | columns: 1; 266 | } 267 | 268 | .aspect-video { 269 | aspect-ratio: 16/9; 270 | } 271 | 272 | .z-20 { 273 | z-index: 20; 274 | } 275 | 276 | .z-90 { 277 | z-index: 90; 278 | } 279 | 280 | .gap-x-1 { 281 | row-gap: 0.2rem; 282 | } 283 | 284 | .items-start { 285 | -webkit-box-align: start; 286 | -webkit-align-items: start; 287 | -moz-box-align: start; 288 | -ms-flex-align: start; 289 | align-items: start; 290 | } 291 | 292 | .items-end { 293 | -webkit-box-align: end; 294 | -webkit-align-items: end; 295 | -moz-box-align: end; 296 | -ms-flex-align: end; 297 | align-items: end; 298 | } 299 | 300 | .grid-cols-6 { 301 | grid-template-columns: repeat(6, 1fr); 302 | } 303 | 304 | .items-evenly { 305 | -webkit-box-align: evenly; 306 | -webkit-align-items: evenly; 307 | -moz-box-align: evenly; 308 | -ms-flex-align: evenly; 309 | align-items: evenly; 310 | } 311 | 312 | .p-2 { 313 | padding: 0.4rem; 314 | } 315 | 316 | .rounded-md { 317 | border: 8px; 318 | } 319 | 320 | .rounded-lg { 321 | border: 10px; 322 | } 323 | 324 | .basis-1\.5 { 325 | -webkit-flex-basis: 1.5; 326 | -ms-flex-preferred-size: 1.5; 327 | flex-basis: 1.5; 328 | } 329 | 330 | .px-10 { 331 | padding: 0 2rem; 332 | } 333 | 334 | .py-12 { 335 | padding: 2.4rem 0; 336 | } 337 | 338 | .basis-8 { 339 | -webkit-flex-basis: 8; 340 | -ms-flex-preferred-size: 8; 341 | flex-basis: 8; 342 | } 343 | 344 | .p-\[3px\] { 345 | padding: 3px; 346 | } 347 | 348 | .gap-x-\[10rem\] { 349 | row-gap: 10rem; 350 | } 351 | 352 | .gap-18 { 353 | gap: 3.6rem; 354 | } 355 | 356 | .gap-y-20 { 357 | -webkit-column-gap: 4rem; 358 | -moz-column-gap: 4rem; 359 | column-gap: 4rem; 360 | } 361 | 362 | .rounded-full { 363 | border: 9999px; 364 | } 365 | 366 | @media only screen and (min-width: 576px) { 367 | .md\:flex-none { 368 | -webkit-box-flex: 0; 369 | -webkit-flex: none; 370 | -moz-box-flex: 0; 371 | -ms-flex: none; 372 | flex: none; 373 | } 374 | .md\:flex-nowrap { 375 | -webkit-flex-wrap: nowrap; 376 | -ms-flex-wrap: nowrap; 377 | flex-wrap: nowrap; 378 | } 379 | .md\:flex-row-reverse { 380 | -webkit-box-orient: horizontal; 381 | -webkit-box-direction: reverse; 382 | -webkit-flex-direction: row-reverse; 383 | -moz-box-orient: horizontal; 384 | -moz-box-direction: reverse; 385 | -ms-flex-direction: row-reverse; 386 | flex-direction: row-reverse; 387 | } 388 | .md\:gap-\[2rem\] { 389 | gap: 2rem; 390 | } 391 | .md\:grid-cols-4 { 392 | grid-template-columns: repeat(4, 1fr); 393 | } 394 | .md\:basis-0\.5 { 395 | -webkit-flex-basis: 0.5; 396 | -ms-flex-preferred-size: 0.5; 397 | flex-basis: 0.5; 398 | } 399 | .md\:basis-1\/12 { 400 | -webkit-flex-basis: 8.3333333333%; 401 | -ms-flex-preferred-size: 8.3333333333%; 402 | flex-basis: 8.3333333333%; 403 | } 404 | .md\:rounded-\[5px\] { 405 | border: 5px; 406 | } 407 | } 408 | @media only screen and (min-width: 992px) { 409 | .lg\:flex-initial { 410 | -webkit-box-flex: 0; 411 | -webkit-flex: 0 1 auto; 412 | -moz-box-flex: 0; 413 | -ms-flex: 0 1 auto; 414 | flex: 0 1 auto; 415 | } 416 | .lg\:flex-row { 417 | -webkit-box-orient: horizontal; 418 | -webkit-box-direction: normal; 419 | -webkit-flex-direction: row; 420 | -moz-box-orient: horizontal; 421 | -moz-box-direction: normal; 422 | -ms-flex-direction: row; 423 | flex-direction: row; 424 | } 425 | .lg\:grid-cols-5 { 426 | grid-template-columns: repeat(5, 1fr); 427 | } 428 | .lg\:basis-3\.5 { 429 | -webkit-flex-basis: 3.5; 430 | -ms-flex-preferred-size: 3.5; 431 | flex-basis: 3.5; 432 | } 433 | .lg\:mb-8 { 434 | margin-bottom: 1.6rem; 435 | } 436 | .lg\:basis-1\/4 { 437 | -webkit-flex-basis: 25%; 438 | -ms-flex-preferred-size: 25%; 439 | flex-basis: 25%; 440 | } 441 | } 442 | @media only screen and (max-width: 575px) { 443 | .sm\:grid-cols-3 { 444 | grid-template-columns: repeat(3, 1fr); 445 | } 446 | .sm\:gap-4 { 447 | gap: 0.8rem; 448 | } 449 | } --------------------------------------------------------------------------------