Showing preview only (2,189K chars total). Download the full file or copy to clipboard to get everything.
Repository: dylanirlbeck/tailwind-ppx
Branch: master
Commit: c3ec90e9cb8d
Files: 54
Total size: 2.1 MB
Directory structure:
gitextract_ls90ifer/
├── .all-contributorsrc
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── bundle-release.js
│ ├── lint_pr.yml
│ ├── pipeline.yml
│ ├── print_esy_cache.js
│ └── release-postinstall.js
├── .gitignore
├── .ocamlformat
├── .spin
├── LICENSE
├── README.md
├── bin/
│ ├── Bin.re
│ ├── UsePpx.re
│ └── dune
├── dune
├── dune-project
├── esy.json
├── js/
│ └── index.js
├── script/
│ ├── release-postinstall.js
│ └── release.sh
├── src/
│ ├── ppx/
│ │ ├── Tailwind_ppx.re
│ │ ├── dune
│ │ ├── levenshtein.re
│ │ ├── option.re
│ │ ├── ppx_config.re
│ │ ├── read_tailwind.re
│ │ └── utils.re
│ └── use_ppx/
│ ├── Paths.re
│ ├── dune
│ └── ext/
│ ├── ext_json_parse.ml
│ ├── ext_json_types.ml
│ └── ext_position.ml
├── tailwind-ppx.opam
├── tailwind-ppx.opam.template
└── test/
├── bucklescript/
│ ├── .gitignore
│ ├── bsconfig.json
│ ├── dune
│ ├── index.css
│ ├── package.json
│ ├── src/
│ │ ├── Demo.re
│ │ ├── index_res_test.res
│ │ └── index_test.re
│ ├── tailwind.config.js
│ └── tailwind_fake.css
├── native/
│ ├── TestRunner.re
│ ├── dune
│ ├── dune-project
│ ├── lib/
│ │ ├── Setup.re
│ │ ├── Test.re
│ │ ├── dune
│ │ └── helpers.ml
│ └── tailwind-ppx-test-native.opam
└── tailwind.css
================================================
FILE CONTENTS
================================================
================================================
FILE: .all-contributorsrc
================================================
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"contributors": [
{
"login": "dylanirlbeck",
"name": "Dylan Irlbeck ",
"avatar_url": "https://avatars0.githubusercontent.com/u/35497479?v=4",
"profile": "https://dev.to/dylanirlbeck",
"contributions": [
"code",
"doc"
]
},
{
"login": "tatchi",
"name": "Corentin Leruth",
"avatar_url": "https://avatars2.githubusercontent.com/u/5595092?v=4",
"profile": "https://github.com/tatchi",
"contributions": [
"code",
"ideas",
"maintenance"
]
},
{
"login": "zth",
"name": "Gabriel Nordeborn",
"avatar_url": "https://avatars2.githubusercontent.com/u/1457626?v=4",
"profile": "https://twitter.com/___zth___",
"contributions": [
"code",
"ideas"
]
},
{
"login": "bdunn313",
"name": "Brad Dunn",
"avatar_url": "https://avatars3.githubusercontent.com/u/867683?v=4",
"profile": "https://github.com/bdunn313",
"contributions": [
"code",
"bug"
]
},
{
"login": "tcoopman",
"name": "Thomas Coopman",
"avatar_url": "https://avatars3.githubusercontent.com/u/45546?v=4",
"profile": "https://infinitetree.eu/",
"contributions": [
"bug",
"doc"
]
},
{
"login": "prometheansacrifice",
"name": "Manas",
"avatar_url": "https://avatars0.githubusercontent.com/u/3097018?v=4",
"profile": "http://prometheansacrifice.me/",
"contributions": [
"doc"
]
},
{
"login": "peterpme",
"name": "Peter Piekarczyk",
"avatar_url": "https://avatars0.githubusercontent.com/u/1211905?v=4",
"profile": "https://peterp.me",
"contributions": [
"ideas"
]
},
{
"login": "pckilgore",
"name": "Patrick Kilgore",
"avatar_url": "https://avatars0.githubusercontent.com/u/2559043?v=4",
"profile": "https://github.com/pckilgore",
"contributions": [
"doc"
]
},
{
"login": "azkane",
"name": "ahzm",
"avatar_url": "https://avatars0.githubusercontent.com/u/3322582?v=4",
"profile": "https://github.com/azkane",
"contributions": [
"code",
"doc",
"ideas"
]
},
{
"login": "joprice",
"name": "Joseph Price",
"avatar_url": "https://avatars1.githubusercontent.com/u/2175555?v=4",
"profile": "https://github.com/joprice",
"contributions": [
"code"
]
},
{
"login": "vdanchenkov",
"name": "Vladimir Danchenkov",
"avatar_url": "https://avatars2.githubusercontent.com/u/2461813?v=4",
"profile": "https://twitter.com/_danchenkov",
"contributions": [
"doc"
]
},
{
"login": "J-Zeitler",
"name": "Jonas Zeitler",
"avatar_url": "https://avatars0.githubusercontent.com/u/1719257?v=4",
"profile": "https://jonas.zeitler.se/",
"contributions": [
"code"
]
},
{
"login": "lozlow",
"name": "Pete Shaw",
"avatar_url": "https://avatars2.githubusercontent.com/u/386319?v=4",
"profile": "https://github.com/lozlow",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"projectName": "tailwind-ppx",
"projectOwner": "dylanirlbeck",
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true
}
================================================
FILE: .gitattributes
================================================
# Supresses the lock folder from the diffs
esy.lock/* linguist-generated=true
# Tell github that .re and .rei files are Reason
*.re linguist-language=Reason
*.rei linguist-language=Reason
# Ignore CSS files
*.css linguist-generated=true
# Disable syntax detection for .spin
.spin linguist-language=Text
# Declare shell files to have LF endings on checkout
# On Windows, the default git setting for `core.autocrlf`
# means that when checking out code, LF endings get converted
# to CRLF. This causes problems for shell scripts, as bash
# gets choked up on the extra `\r` character.
*.css eol=lf
================================================
FILE: .github/workflows/bundle-release.js
================================================
const fs = require("fs");
const path = require("path");
console.log("Creating package.json");
// From the project root pwd
const mainPackageJsonPath = fs.existsSync("esy.json")
? "esy.json"
: "package.json";
const exists = fs.existsSync(mainPackageJsonPath);
if (!exists) {
console.error("No package.json or esy.json at " + mainPackageJsonPath);
process.exit(1);
}
// Now require from this script's location.
const mainPackageJson = require(path.join("..", "..", mainPackageJsonPath));
const bins = Array.isArray(mainPackageJson.esy.release.bin)
? mainPackageJson.esy.release.bin.reduce(
(acc, curr) => Object.assign({ [curr]: "bin/" + curr }, acc),
{}
)
: Object.keys(mainPackageJson.esy.release.bin).reduce(
(acc, currKey) =>
Object.assign(
{ [currKey]: "bin/" + mainPackageJson.esy.release.bin[currKey] },
acc
),
{}
);
const rewritePrefix =
mainPackageJson.esy &&
mainPackageJson.esy.release &&
mainPackageJson.esy.release.rewritePrefix;
const packageJson = JSON.stringify(
{
name: "@dylanirlbeck/tailwind-ppx",
version: mainPackageJson.version,
license: mainPackageJson.license,
description: mainPackageJson.description,
repository: mainPackageJson.repository,
repository: mainPackageJson.repository,
main: "js/index.js",
scripts: {
postinstall: rewritePrefix
? "ESY_RELEASE_REWRITE_PREFIX=true node ./postinstall.js"
: "node ./postinstall.js"
},
bin: bins,
files: [
"_export/",
"bin/",
"js/",
"postinstall.js",
"esyInstallRelease.js",
"platform-linux/",
"platform-darwin/",
"platform-windows-x64/"
]
},
null,
2
);
fs.writeFileSync(
path.join(__dirname, "..", "..", "_release", "package.json"),
packageJson,
{
encoding: "utf8"
}
);
try {
console.log("Copying LICENSE");
fs.copyFileSync(
path.join(__dirname, "..", "..", "LICENSE"),
path.join(__dirname, "..", "..", "_release", "LICENSE")
);
} catch (e) {
console.warn("No LICENSE found");
}
console.log("Copying README.md");
fs.copyFileSync(
path.join(__dirname, "..", "..", "README.md"),
path.join(__dirname, "..", "..", "_release", "README.md")
);
console.log("Copying postinstall.js");
fs.copyFileSync(
path.join(__dirname, "release-postinstall.js"),
path.join(__dirname, "..", "..", "_release", "postinstall.js")
);
console.log("Copying js folder");
fs.mkdirSync(path.join(__dirname, "..", "..", "_release", "js"));
fs.copyFileSync(
path.join(__dirname, "..", "..", "js", "index.js"),
path.join(__dirname, "..", "..", "_release", "js", "index.js")
);
console.log("Creating placeholder files");
const placeholderFile = `:; echo "You need to have postinstall enabled"; exit $?
@ECHO OFF
ECHO You need to have postinstall enabled`;
fs.mkdirSync(path.join(__dirname, "..", "..", "_release", "bin"));
Object.keys(bins).forEach(name => {
if (bins[name]) {
const binPath = path.join(__dirname, "..", "..", "_release", bins[name]);
fs.writeFileSync(binPath, placeholderFile);
fs.chmodSync(binPath, 0777);
} else {
console.log("bins[name] name=" + name + " was empty. Weird.");
console.log(bins);
}
});
================================================
FILE: .github/workflows/lint_pr.yml
================================================
name: Lint and format Pull Request
on: [pull_request]
jobs:
lint:
name: lint lockdir
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v1
with:
node-version: 12
- uses: actions/checkout@v1
- name: Install esy
run: npm install -g esy
- name: Update lockdir
run: |
esy install
git add esy.lock
- name: Print esy cache
id: print_esy_cache
run: node .github/workflows/print_esy_cache.js
- name: Try to restore dependencies cache
id: deps-cache-macos
uses: actions/cache@v1
with:
path: ${{ steps.print_esy_cache.outputs.esy_cache }}
key: ${{ matrix.os }}-${{ hashFiles('**/index.json')}}
- name: format sources files
run: |
esy format
git add "*.re" "*.ml"
- name: Check that lock directory are up-to-date.
run: git diff --exit-code --cached
- name: Push if it is not
if: failure()
run: |
git remote set-url --push origin https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY
git config user.name "Github Runner"
git config user.email "runner@runner.github.com"
git commit -am "Update lockdir"
git status
git push origin HEAD:$GITHUB_HEAD_REF
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Display status should be clean
if: success()
run: git status
- uses: actions/github-script@0.3.0
if: failure()
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
github.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `I have updated your lock dirs and formatted the code. Please @${context.actor} pull the last commit before pushing any more changes.`
});
================================================
FILE: .github/workflows/pipeline.yml
================================================
name: tailwind-ppx pipeline
on:
push:
branches:
- master
tags:
- v*
pull_request:
branches:
- master
jobs:
build_and_test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: 12
- name: Install esy
run: npm install -g esy@0.6.4
- name: Try to restore install cache
uses: actions/cache@v2
with:
path: ~/.esy/source
key: source-${{ matrix.os }}-${{ hashFiles('**/index.json') }}
- name: Install dependencies
run: esy install
- name: Print esy cache
id: print_esy_cache
run: node .github/workflows/print_esy_cache.js
- name: Try to restore dependencies cache
id: deps-cache
uses: actions/cache@v2
with:
path: ${{ steps.print_esy_cache.outputs.esy_cache }}
key: build-${{ matrix.os }}-${{ hashFiles('**/index.json') }}
restore-keys: build-${{ matrix.os }}-
- name: Build dependencies
if: steps.deps-cache.outputs.cache-hit != 'true'
run: esy build-dependencies
- name: Build project
run: esy build
- name: Run Native tests
run: esy test_native
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ matrix.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ matrix.os }}-yarn-
- name: Install Yarn dependencies
run: yarn install --frozen-lockfile
working-directory: ./test/bucklescript
- name: test-bucklescript
run: |
yarn build
yarn test
working-directory: ./test/bucklescript
# Create the NPM package for this platform
- name: Create npm package
run: esy npm-release
- uses: actions/upload-artifact@v2
with:
name: ${{ matrix.os }}
path: _release/
# Cleanup build cache if dependencies have changed
- name: Clean build cache
if: steps.deps-cache.outputs.cache-hit != 'true'
run: esy cleanup .
prepare-publish:
name: Prepare publish to npm
needs: build_and_test
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v1
with:
node-version: 12
- uses: actions/checkout@v2
# Download platform artifacts
- name: Download Linux release
uses: actions/download-artifact@v2
with:
name: ubuntu-latest
path: ubuntu-latest
- name: Download Windows release
uses: actions/download-artifact@v2
with:
name: windows-latest
path: windows-latest
- name: Download macOS release
uses: actions/download-artifact@v2
with:
name: macos-latest
path: macos-latest
# Move artifacts in place
- name: Move artifacts
run: |
mkdir -p _release/platform-linux
mkdir -p _release/platform-windows-x64
mkdir -p _release/platform-darwin
cp -a ubuntu-latest/. _release/platform-linux
cp -a windows-latest/. _release/platform-windows-x64
cp -a macos-latest/. _release/platform-darwin
- name: Prepare package
run: node .github/workflows/bundle-release.js
# Create a npm package that can easily be published and tested
- name: npm pack
run: npm pack .
working-directory: _release
- name: move package
run: mv _release/*.tgz tailwind-ppx.tgz
# Upload artifacts
- uses: actions/upload-artifact@v2
with:
name: release
path: _release/
- uses: actions/upload-artifact@v2
with:
name: release-tarball
path: tailwind-ppx.tgz
test-platform:
name: Test installing ${{ matrix.os }}
needs: prepare-publish
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
steps:
- uses: actions/setup-node@v1
with:
node-version: 12
- name: Download ${{ matrix.os }} release
uses: actions/download-artifact@v2
with:
name: release-tarball
- name: test install
run: npm -g install ./tailwind-ppx.tgz bs-platform
- name: bootstrap test project
run: bsb -init test-tailwind-ppx -theme basic-reason
- name: install bs-platform locally
run: npm install bs-platform
working-directory: ./test-tailwind-ppx
- name: build project
run: npm run build
working-directory: ./test-tailwind-ppx
- name: test tailwind-ppx binary
run: npx tailwind-ppx --help
- name: test use-tailwind-ppx binary
run: npx use-tailwind-ppx
working-directory: ./test-tailwind-ppx
publish:
needs: prepare-publish
if: github.event_name != 'pull_request' && startsWith(github.ref, 'refs/tags/')
name: (only on release) Publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Download release
uses: actions/download-artifact@v2
with:
name: release
path: release
- name: Zip release folder
run: zip -r release.zip release
- name: Create GitHub release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: Upload ubuntu-latest to Github release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: release.zip
asset_name: release.zip
asset_content_type: application/gzip
- name: Publish NPM package
run: |
npm config set //registry.npmjs.org/:_authToken=$NODE_AUTH_TOKEN
npm config set scope "@dylanirlbeck"
npm config list
npm publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
working-directory: ./release
================================================
FILE: .github/workflows/print_esy_cache.js
================================================
const fs = require("fs");
const os = require("os");
const path = require("path");
const ESY_FOLDER = process.env.ESY__PREFIX
? process.env.ESY__PREFIX
: path.join(os.homedir(), ".esy");
const esy3 = fs
.readdirSync(ESY_FOLDER)
.filter(name => name.length > 0 && name[0] === "3")
.sort()
.pop();
console.log(`::set-output name=esy_cache::${path.join(ESY_FOLDER, esy3, "i")}`);
================================================
FILE: .github/workflows/release-postinstall.js
================================================
/**
* release-postinstall.js
*
* XXX: We want to keep this script installable at least with node 4.x.
*
* This script is bundled with the `npm` package and executed on release.
* Since we have a 'fat' NPM package (with all platform binaries bundled),
* this postinstall script extracts them and puts the current platform's
* bits in the right place.
*/
var path = require('path');
var cp = require('child_process');
var fs = require('fs');
var os = require('os');
var platform = process.platform;
var packageJson = require('./package.json');
var binariesToCopy = Object.keys(packageJson.bin)
.map(function (name) {
return packageJson.bin[name];
})
.concat(['esyInstallRelease.js']);
var foldersToCopy = ['bin', '_export'];
function copyRecursive(srcDir, dstDir) {
var results = [];
var list = fs.readdirSync(srcDir);
var src, dst;
list.forEach(function (file) {
src = path.join(srcDir, file);
dst = path.join(dstDir, file);
var stat = fs.statSync(src);
if (stat && stat.isDirectory()) {
try {
fs.mkdirSync(dst);
} catch (e) {
console.log('directory already exists: ' + dst);
console.error(e);
}
results = results.concat(copyRecursive(src, dst));
} else {
try {
fs.writeFileSync(dst, fs.readFileSync(src));
} catch (e) {
console.log("could't copy file: " + dst);
console.error(e);
}
results.push(src);
}
});
return results;
}
/**
* Since os.arch returns node binary's target arch, not
* the system arch.
* Credits: https://github.com/feross/arch/blob/af080ff61346315559451715c5393d8e86a6d33c/index.js#L10-L58
*/
function arch() {
/**
* The running binary is 64-bit, so the OS is clearly 64-bit.
*/
if (process.arch === 'x64') {
return 'x64';
}
/**
* All recent versions of Mac OS are 64-bit.
*/
if (process.platform === 'darwin') {
return 'x64';
}
/**
* On Windows, the most reliable way to detect a 64-bit OS from within a 32-bit
* app is based on the presence of a WOW64 file: %SystemRoot%\SysNative.
* See: https://twitter.com/feross/status/776949077208510464
*/
if (process.platform === 'win32') {
var useEnv = false;
try {
useEnv = !!(
process.env.SYSTEMROOT && fs.statSync(process.env.SYSTEMROOT)
);
} catch (err) {}
var sysRoot = useEnv ? process.env.SYSTEMROOT : 'C:\\Windows';
// If %SystemRoot%\SysNative exists, we are in a WOW64 FS Redirected application.
var isWOW64 = false;
try {
isWOW64 = !!fs.statSync(path.join(sysRoot, 'sysnative'));
} catch (err) {}
return isWOW64 ? 'x64' : 'x86';
}
/**
* On Linux, use the `getconf` command to get the architecture.
*/
if (process.platform === 'linux') {
var output = cp.execSync('getconf LONG_BIT', { encoding: 'utf8' });
return output === '64\n' ? 'x64' : 'x86';
}
/**
* If none of the above, assume the architecture is 32-bit.
*/
return 'x86';
}
// implementing it b/c we don't want to depend on fs.copyFileSync which appears
// only in node@8.x
function copyFileSync(sourcePath, destPath) {
var data;
try {
data = fs.readFileSync(sourcePath);
} catch (e) {
console.log("Couldn't find " + sourcePath + ' trying with .exe');
data = fs.readFileSync(sourcePath + '.exe');
sourcePath = sourcePath + '.exe';
}
var stat = fs.statSync(sourcePath);
fs.writeFileSync(destPath, data);
fs.chmodSync(destPath, stat.mode);
}
var copyPlatformBinaries = (platformPath) => {
var platformBuildPath = path.join(__dirname, 'platform-' + platformPath);
foldersToCopy.forEach((folderPath) => {
var sourcePath = path.join(platformBuildPath, folderPath);
var destPath = path.join(__dirname, folderPath);
copyRecursive(sourcePath, destPath);
});
binariesToCopy.forEach((binaryPath) => {
var sourcePath = path.join(platformBuildPath, binaryPath);
var destPath = path.join(__dirname, binaryPath);
if (fs.existsSync(destPath)) {
fs.unlinkSync(destPath);
}
copyFileSync(sourcePath, destPath);
fs.chmodSync(destPath, 0777);
});
};
if (fs.existsSync('_export')) {
// remove previous binary
fs.readdirSync('_export').forEach((file) => {
fs.unlinkSync(`_export/${file}`);
});
} else {
try {
fs.mkdirSync('_export');
} catch (e) {
console.log('Could not create _export folder');
}
}
switch (platform) {
case 'win32':
if (arch() !== 'x64') {
console.warn('error: x86 is currently not supported on Windows');
process.exit(1);
}
copyPlatformBinaries('windows-x64');
break;
case 'linux':
case 'darwin':
copyPlatformBinaries(platform);
break;
default:
console.warn('error: no release built for the ' + platform + ' platform');
process.exit(1);
}
require('./esyInstallRelease');
================================================
FILE: .gitignore
================================================
# ocamlbuild working directory
_build/
# ocamlbuild targets
*.byte
*.native
# Merlin configuring file for Vim and Emacs
.merlin
# Dune generated files
*.install
# Local OPAM switch
_opam/
# Esy generated files
_esy/
_release/
_export/
npm-debug.log
yarn-error.log
node_modules/
.tailwind_ppx_cache
================================================
FILE: .ocamlformat
================================================
================================================
FILE: .spin
================================================
(Source ppx)
(Cfg_str project_name tailwind-ppx)
(Cfg_str project_slug tailwind-ppx)
(Cfg_str project_description"A short, but powerful statement about your project")
(Cfg_str syntax Reason)
(Cfg_str package_manager Esy)
(Cfg_str test_framework Rely)
(Cfg_str ci_cd Github)
(Cfg_str author_name"Dylan Irlbeck")
(Cfg_str github_username dylanirlbeck)
(Cfg_str npm_username dylanirlbeck)
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Dylan Irlbeck
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
**Note: As of 08/16/2021, `tailwind-ppx` is now in archive-mode. I have been out of the Reason/ReScript community for some time now, but if anyone wants to take over as owner please let me know. Thank you to all involved for making this project successful.**
---
# tailwind-ppx
[](https://github.com/dylanirlbeck/tailwind-ppx/actions)
[](https://badge.fury.io/js/%40dylanirlbeck%2Ftailwind-ppx)
[](#contributors-)
A Reason/OCaml [Pre-Processor eXtension (PPX)](https://blog.hackages.io/reasonml-ppx-8ecd663d5640) that validates your Tailwind classes at compile-time.
<p align="center"><img src="assets/demo.png" /></p>
## Table of Contents
- [Features (Current and Upcoming)](#features)
- [Usage](#usage)
- [Configuration](#configuration)
- [Installation](#installation)
- [Frequently Asked Questions (FAQ)](#faq)
- [Developing](#developing)
- [Contributors](#contributors-)
- [Examples and Related Projects](#other)
## Features
**Current**
- Checks for invalid class names (and suggestions for valid ones!)
- Checks for duplicate class names
- Always in-sync with your `tailwind.css` file (just make sure to re-build!)
- [Automatic purging of unused class names](#getting-ready-for-production) (with PurgeCSS and `tailwind-ppx`'s custom extractor function)
- [Ships with an integration script](#integration-script) that converts all your
existing `className="..."` to `className=[%tw "..."]`
**Upcoming**
- [Better integration with PostCSS](https://github.com/dylanirlbeck/tailwind-ppx/issues/62)
- Checks for redundant class names (like having both `flex-row` and `flex-col`)
- Checks for class name dependencies (like having `flex-row` without `flex`)
If you have ideas for new features, please [open an issue](https://github.com/dylanirlbeck/tailwind-ppx/issues)!
## Usage
`tailwind_ppx` implements a ppx (`%tw`) that validates your Tailwind CSS classes at compile time.
For example, for the following (condensed) `tailwind.css` file:
```css
.flex {
display: flex;
}
.flex-row {
flex-direction: row;
}
```
`tailwind-ppx` will provide validation for your desired class names. See these
examples:
```reason
// Example 1
<Component className=[%tw "flex flex-row"] /> // This is ok!
// Example 2
<Component className=[%tw "flex flex-ro"] /> // ERROR: Class name not found: flex-ro. Did you mean flex-row?
// Example 3
<Component className=[%tw "flex flex-row flex"] /> // ERROR: Duplicate class name: flex
```
Finally, `tailwind-ppx` requires your **generated** `tailwind.css` file to exist somewhere in the
project hierarchy. Though not required, it's recommended that you [configure the
path](#-path) to your `tailwind.css` file (relative to your project root).
### Getting ready for production
As [outlined in the Tailwind docs](https://tailwindcss.com/docs/controlling-file-size/), when preparing for production you'll want to make sure that the only CSS from Tailwind that ends up in your bundle is CSS that you _actually use_ in your code.
First, take a second to read the [section on setting up Purgecss from the Tailwind docs](https://tailwindcss.com/docs/controlling-file-size/#setting-up-purgecss). In order to help with the process outlined in the docs, this package ships with a default extractor function that'll take care of ensuring that any CSS from Tailwind that you aren't using with this PPX can be purged from your production CSS bundle. You enable it by slightly modifying the official example of how to set up your `postcss.config.js`:
```javascript
// postcss.config.js
const purgecss = require("@fullhuman/postcss-purgecss")({
// Specify the paths to all ReasonML code where you're using this PPX.
content: ["./src/**/*.re"],
// Include the extractor from this package
defaultExtractor: require("@dylanirlbeck/tailwind-ppx").extractor
});
module.exports = {
plugins: [
require("tailwindcss"),
require("autoprefixer"),
...(process.env.NODE_ENV === "production" ? [purgecss] : [])
]
};
```
Doing this will ensure that you only ship CSS from Tailwind to production that you're actually using with this PPX.
### Moving or changing your `tailwind.css` file
If your `tailwind.css` file changes (or you move it) you'll need to rebuild your
project - for example, `bsb -clean-world` and `bsb -make-world` if in BuckleScript.
At this time, `tailwind-ppx` does not automatically watch for changes, though this is on
the roadmap.
Alternatively, you can add the following rules to you bsconfig.json to re-trigger builds
```json
{
"sources": [
{
"dir": "src",
"subdirs": true,
"generators": [
{
"name": "gen-tailwind",
"edge": ["tailwind.css", ":", "styles.css"]
}
]
}
],
"generators": [
{
"name": "gen-tailwind",
"command": "tailwindcss build $in -o $out"
}
]
}
```
If you have a custom tailwind config file, you'll need to pass it to the tailwindcss command:
```json
{
"name": "gen-tailwind",
"command": "tailwindcss build $in -o $out -c ../../tailwind.config.js"
}
```
You might have to [specify the path to `tailwind.css`](#-path).
### Autocompletion (Neovim only)
If you're a Neovim user, you can download the [`coc-tailwindcss`](https://github.com/iamcco/coc-tailwindcss) extension to get class name autocompletion while using `tailwind-ppx` - just make sure to define a `tailwind.config.js` file. See the example below!
<img src="assets/autocompletion.png" height="600" width="800">
### Ignore `.tailwind_ppx_cache` in your version control
`tailwind-ppx` will generate a `.tailwind_ppx_cache` folder in your project root
to optimize the validation performance. If you're using a version control
system, you don't need to check it in.
## Configuration
### -path
By default, `tailwind-ppx` looks for your `tailwind.css` file in the root
directory. If `tailwind.css` lives elsewhere (or the name of your generated CSS file is different), you'll need to specify the file path in your `bsconfig.json`.
```json
"ppx-flags": [
["tailwind-ppx", "-path ../path/to/tailwind.css",]
],
```
## Installation
The most likely use case for `tailwind-ppx` is inside ReasonReact projects
(using BuckleScript). To get started, we recommend cloning our [demo
project](https://github.com/dylanirlbeck/tailwind-ppx-demo).
### With `yarn` or `npm` on Bucklescript projects (recommended)
Install the PPX with `yarn` or `npm`
```bash
yarn add --dev @dylanirlbeck/tailwind-ppx
# Or
npm install --dev @dylanirlbeck/tailwind-ppx
```
And add the PPX in your `bsconfig.json` file:
```json
{
"ppx-flags": ["tailwind-ppx"]
}
```
### Integration script
The `@dylanirlbeck/tailwind-ppx` NPM package ships with an executable that, when
run in a BuckleScript project, turns all instances of `className="..."` into
`className=[%tw "..."]`. The script is designed to make it easy to immediately
introduce `tailwind-ppx` into an existing codebase.
You can use this script by running the following command from the root of your
project (just make sure you've installed the NPM package).
```
yarn use-tailwind-ppx
# Or
npx use-tailwind-ppx
```
> Note that you'll need both a `bsconfig.json` to exist in the project hierarchy
> and compiled project with `bsb -make-world` (so the `lib/` directory exists in
> the project root) for the script to work properly.
## FAQ
- **How can I conditionally add classes?**
This feature is out of scope for `tailwind-ppx`; instead, we recommend you use
[`re-classnames`](https://github.com/MinimaHQ/re-classnames) in combination
with `tailwind-ppx`. See the example below:
```reason
module SomeComponent = {
[@react.component]
let make = (~someBool) => {
let className =
Cn.(
[%tw "text-blue-500"]->on(someBool)
+ [%tw "text-gray-500"]->on(!someBool)
);
<div className />;
};
};
```
- **How can I use custom CSS classes?**
`tailwind-ppx` directly parses your generated `tailwind.css` file, which means
that **all** CSS classes will be validated by the PPX, including custom class
names defined in your base `index.css/styles.css` file. In short, if the class
is in your `tailwind.css` file, it will be validated correctly by the ppx.
Example:
```reason
<Component className=[%tw "flex flex-row customLayou"] /> // ERROR: Class name not found: customLayou. Did you mean customLayout?
```
## Developing
After cloning the repository, you should run `esy` to install the project dependencies. After that, you should be good to start developing!
### Relevant commands
- `esy build` -> Builds the project
- `esy format` -> Formats the entire project with `ocamlformat` and `refmt`
- `esy watch` -> Watches for changes to Reason/OCaml files in the entire project, including in the `/test` directory
- `esy test_native` -> Runs the native tests (in `test/native`)
- `cd test/bucklescript && yarn test` -> Runs the BuckleScript tests (in `test/bucklescript`)
> Note that if you pull requests are not formatted properly, or the `esy.lock`
> is out-of-date, GitHub actions will automatically format your code by pushing
> up a new commit.
### Releasing (for maintainers)
1. Bump the version of the ppx in `esy.json` on `master` (we use [semantic versioning](https://semver.org/))
2. Create and push a new tag
```
$ git checkout master
$ git tag vx.y.z
$ git push origin vx.y.z
```
3. [Create detailed release notes](https://github.com/dylanirlbeck/tailwind-ppx/releases) for the new version, following the `Added/Changed/Fixed/Removed` format. Note that the new version of the PPX will automatically be pushed to NPM and a release will be created on GitHub.
4. Make sure that for any merged pull requests/closed issues were noticed by the `all-contributors` bot -- see [this PR](https://github.com/dylanirlbeck/tailwind-ppx/pull/116#issuecomment-657551562) for an example of adding a new contributor who's PR was merged.
## Contributors ✨
Thanks goes to these wonderful people:
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://dev.to/dylanirlbeck"><img src="https://avatars0.githubusercontent.com/u/35497479?v=4" width="100px;" alt=""/><br /><sub><b>Dylan Irlbeck </b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=dylanirlbeck" title="Code">💻</a> <a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=dylanirlbeck" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/tatchi"><img src="https://avatars2.githubusercontent.com/u/5595092?v=4" width="100px;" alt=""/><br /><sub><b>Corentin Leruth</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=tatchi" title="Code">💻</a> <a href="#ideas-tatchi" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-tatchi" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://twitter.com/___zth___"><img src="https://avatars2.githubusercontent.com/u/1457626?v=4" width="100px;" alt=""/><br /><sub><b>Gabriel Nordeborn</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=zth" title="Code">💻</a> <a href="#ideas-zth" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/bdunn313"><img src="https://avatars3.githubusercontent.com/u/867683?v=4" width="100px;" alt=""/><br /><sub><b>Brad Dunn</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=bdunn313" title="Code">💻</a> <a href="https://github.com/dylanirlbeck/tailwind-ppx/issues?q=author%3Abdunn313" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://infinitetree.eu/"><img src="https://avatars3.githubusercontent.com/u/45546?v=4" width="100px;" alt=""/><br /><sub><b>Thomas Coopman</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/issues?q=author%3Atcoopman" title="Bug reports">🐛</a> <a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=tcoopman" title="Documentation">📖</a></td>
<td align="center"><a href="http://prometheansacrifice.me/"><img src="https://avatars0.githubusercontent.com/u/3097018?v=4" width="100px;" alt=""/><br /><sub><b>Manas</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=prometheansacrifice" title="Documentation">📖</a></td>
<td align="center"><a href="https://peterp.me"><img src="https://avatars0.githubusercontent.com/u/1211905?v=4" width="100px;" alt=""/><br /><sub><b>Peter Piekarczyk</b></sub></a><br /><a href="#ideas-peterpme" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/pckilgore"><img src="https://avatars0.githubusercontent.com/u/2559043?v=4" width="100px;" alt=""/><br /><sub><b>Patrick Kilgore</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=pckilgore" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/azkane"><img src="https://avatars0.githubusercontent.com/u/3322582?v=4" width="100px;" alt=""/><br /><sub><b>ahzm</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=azkane" title="Code">💻</a> <a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=azkane" title="Documentation">📖</a> <a href="#ideas-azkane" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/joprice"><img src="https://avatars1.githubusercontent.com/u/2175555?v=4" width="100px;" alt=""/><br /><sub><b>Joseph Price</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=joprice" title="Code">💻</a></td>
<td align="center"><a href="https://twitter.com/_danchenkov"><img src="https://avatars2.githubusercontent.com/u/2461813?v=4" width="100px;" alt=""/><br /><sub><b>Vladimir Danchenkov</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=vdanchenkov" title="Documentation">📖</a></td>
<td align="center"><a href="https://jonas.zeitler.se/"><img src="https://avatars0.githubusercontent.com/u/1719257?v=4" width="100px;" alt=""/><br /><sub><b>Jonas Zeitler</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=J-Zeitler" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lozlow"><img src="https://avatars2.githubusercontent.com/u/386319?v=4" width="100px;" alt=""/><br /><sub><b>Pete Shaw</b></sub></a><br /><a href="https://github.com/dylanirlbeck/tailwind-ppx/commits?author=lozlow" title="Code">💻</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
## Other
### Examples
These projects are using `tailwind-ppx` throughout the code base:
- [my-first-pr](https://github.com/dylanirlbeck/my-first-pr)
### Related Projects
The following amazing projects provided a lot of inspiration; I recommend you check them out!
- [ocaml-css-parser](https://github.com/astrada/ocaml-css-parser)
- [styled-ppx](https://github.com/davesnx/styled-ppx)
- [graphql-ppx](https://github.com/reasonml-community/graphql_ppx)
================================================
FILE: bin/Bin.re
================================================
open Migrate_parsetree;
// Source: https://github.com/reasonml-community/graphql_ppx/blob/master/src/bucklescript_bin/Bin.re
let argv =
switch (Sys.argv |> Array.to_list) {
| [program, ...rest] =>
switch (List.rev(rest)) {
| [output_file, input_file, ...args] =>
List.concat([
[program],
List.rev(args),
[input_file, "-o", output_file, "--dump-ast", "--embed-errors"],
])
|> Array.of_list
| _ => Sys.argv
}
| _ => Sys.argv
};
let () = Driver.run_main(~argv, ());
================================================
FILE: bin/UsePpx.re
================================================
/** Reads the contents of a file */
let readFile = path => {
let ch = open_in(path);
let s = really_input_string(ch, in_channel_length(ch));
close_in(ch);
s;
};
/** Writes the contents of a file */
let writeFile = (path, newContents) => {
let ch = open_out(path);
output_string(ch, newContents);
close_out(ch);
};
let numFilesTouched = ref(0);
let transformClassName = file => {
let contents = readFile(file);
let r = Str.regexp("className=\"\\(.*\\)\"");
let newContents =
Str.global_replace(r, "className=[%tw \"\\1\"]", contents);
if (!Digest.equal(Digest.string(contents), Digest.string(newContents))) {
numFilesTouched := numFilesTouched^ + 1;
};
writeFile(file, newContents);
};
let run = () => {
print_endline("Running...");
let (+++) = Filename.concat;
Paths.setProjectRoot();
let sourceDirs = Paths.readSourceDirs(~configSources=None);
sourceDirs
|> List.iter(sourceDir => {
let files =
switch (Sys.readdir(sourceDir) |> Array.to_list) {
| files => files
| exception (Sys_error(_)) => []
};
let reasonFiles =
files |> List.filter(x => Filename.check_suffix(x, ".re"));
reasonFiles
|> List.iter(reasonFile => {
let reasonFilePath = sourceDir +++ reasonFile;
transformClassName(reasonFilePath);
});
});
print_endline(
"Number of files touched: " ++ string_of_int(numFilesTouched^),
);
};
run();
================================================
FILE: bin/dune
================================================
(executable
(name Bin)
(modules Bin)
(public_name tailwind-ppx)
(libraries tailwind-ppx.lib)
(ocamlopt_flags (-linkall))
(package tailwind-ppx))
(executable
(name UsePpx)
(modules UsePpx)
(public_name use-tailwind-ppx)
(libraries str use_ppx_lib)
(ocamlopt_flags (-linkall))
(package tailwind-ppx))
================================================
FILE: dune
================================================
(dirs :standard \ node_modules _build)
================================================
FILE: dune-project
================================================
(lang dune 2.5)
================================================
FILE: esy.json
================================================
{
"name": "tailwind-ppx",
"version": "0.8.4",
"description": "Reason PPX that validates your Tailwind classes at compile-time",
"author": "Dylan Irlbeck <dylanirlbeck@gmail.com>",
"license": "MIT",
"homepage": "https://github.com/dylanirlbeck/tailwind-ppx",
"bugs": { "url": "https://github.com/dylanirlbeck/tailwind-ppx/issues" },
"repository": {
"type": "git",
"url": "https://github.com/dylanirlbeck/tailwind-ppx.git"
},
"esy": {
"build": "dune build -p #{self.name}",
"buildsInSource": "_build",
"release": { "bin": ["tailwind-ppx", "use-tailwind-ppx"] }
},
"dependencies": {
"@opam/dune": "2.5.1",
"@opam/fmt": "0.8.8",
"@opam/menhir": "*",
"@opam/ocaml-migrate-parsetree": "^1.7.0",
"@esy-ocaml/reason": "~3.6.0",
"@opam/css-parser": "^0.2.4"
},
"devDependencies": {
"ocaml": "~4.9.1",
"@opam/ocamlformat": "*",
"@opam/merlin": "*",
"@opam/utop": "*",
"@reason-native/rely": "*",
"refmterr": "*"
},
"scripts": {
"test_native": "esy #{os == 'windows' ? 'b' : ''} dune exec test/native/TestRunner.exe",
"build_all": "esy dune b @all",
"format": "esy dune build @fmt --auto-promote",
"clean": "esy dune clean",
"watch": "esy dune build @all -p #{self.name} --watch",
"release": "./script/release.sh",
"postinstall": "node postinstall.js"
}
}
================================================
FILE: js/index.js
================================================
function extractor(content) {
const matchedContent = content.match(/(?<=\%tw\(?\s*")[^\"]*(?=\")/g);
if (matchedContent) {
return matchedContent.reduce((acc, curr) => {
curr.split(" ").forEach(p => {
const trimmed = p.trim();
if (trimmed !== "") {
acc.push(trimmed);
}
});
return acc;
}, []);
}
return [];
}
module.exports = { extractor };
================================================
FILE: script/release-postinstall.js
================================================
#!/usr/bin/env node
const path = require("path");
const cp = require("child_process");
const fs = require("fs");
const platform = process.platform;
const binariesToCopy = ["tailwind-ppx", "use-tailwind-ppx"];
function find_arch() {
// The running binary is 64-bit, so the OS is clearly 64-bit.
if (process.arch === "x64") {
return "x64";
}
// All recent versions of Mac OS are 64-bit.
if (process.platform === "darwin") {
return "x64";
}
// On Windows, the most reliable way to detect a 64-bit OS from within a 32-bit
// app is based on the presence of a WOW64 file: %SystemRoot%\SysNative.
// See: https://twitter.com/feross/status/776949077208510464
if (process.platform === "win32") {
var useEnv = false;
try {
useEnv = !!(
process.env.SYSTEMROOT && fs.statSync(process.env.SYSTEMROOT)
);
} catch (err) {}
const sysRoot = useEnv ? process.env.SYSTEMROOT : "C:\\Windows";
// If %SystemRoot%\SysNative exists, we are in a WOW64 FS Redirected application.
const isWOW64 = false;
try {
isWOW64 = !!fs.statSync(path.join(sysRoot, "sysnative"));
} catch (err) {}
return isWOW64 ? "x64" : "x86";
}
if (process.platform === "linux") {
const output = cp.execSync("getconf LONG_BIT", { encoding: "utf8" });
return output === "64\n" ? "x64" : "x86";
}
return "x86";
}
// Implementing it b/c we don"t want to depend on fs.copyFileSync which appears only in node@8.x
function copyFileSync(sourcePath, destPath) {
const data = fs.readFileSync(sourcePath);
const stat = fs.statSync(sourcePath);
fs.writeFileSync(destPath, data);
fs.chmodSync(destPath, stat.mode);
}
const copyPlatformBinaries = (platformPath) => {
binariesToCopy.forEach((binaryPath) => {
if (process.platform === "win32") {
binaryPath += ".exe";
}
const sourcePath = path.join(platformPath, binaryPath);
const destPath = path.join(__dirname, binaryPath);
if (fs.existsSync(destPath)) {
fs.unlinkSync(destPath);
}
copyFileSync(sourcePath, destPath);
fs.chmodSync(destPath, 0o755);
});
};
const arch = find_arch();
const getPlatformFolderName = () => {
if (platform === "win32") {
return "platform-windows-" + arch;
}
return "platform-" + platform + "-" + arch;
};
const platformPath = path.join(__dirname, getPlatformFolderName());
const supported = fs.existsSync(platformPath);
if (!supported) {
console.error("tailwind-ppx does not support this platform :(");
console.error("");
console.error("If you want tailwind-ppx to support this platform natively,");
console.error("please open an issue at our repository, linked above.");
console.error("Specify that you are on the " + platform + " platform,");
console.error("and on the " + arch + " architecture.");
process.exit(1);
}
copyPlatformBinaries(platformPath);
================================================
FILE: script/release.sh
================================================
#!/bin/bash
set -e
if [ -d ".git" ]; then
changes=$(git status --porcelain)
branch=$(git rev-parse --abbrev-ref HEAD)
if [ -n "${changes}" ]; then
echo "Please commit staged files prior to bumping"
exit 1
elif [ "${branch}" != "master" ]; then
echo "Please run the release script on master"
exit 1
else
esy x dune-release tag
esy x dune-release distrib
esy x dune-release publish -y
esy x dune-release opam pkg
esy x dune-release opam submit --no-auto-open -y
fi
else
echo "This project is not a git repository. Run `git init` first to be able to release."
exit 1
fi
================================================
FILE: src/ppx/Tailwind_ppx.re
================================================
open Migrate_parsetree;
open Ast_409;
open Ast_mapper;
open Parsetree;
open Utils;
let expr = (mapper, e) =>
switch (e.pexp_desc) {
| Pexp_extension((
{txt: "tw", _},
PStr([
{
pstr_desc:
Pstr_eval(
{
pexp_loc: loc,
// Force the PPX to only recognize strings inside quotations
pexp_desc: Pexp_constant(Pconst_string(classNames, None)),
_,
},
_,
),
_,
},
]),
)) =>
// Contents of tailwind.css file
let tailwindFileContent =
switch (Lazy.force(Read_tailwind.getTailwind)) {
| Some(file) => file
| None =>
raise(
Location.Error(
Location.error(
~loc,
"tailwind.css file not found in project hierarchy. You may need to manually set the path to the file with the -path argument.",
),
),
)
};
validate(~classNames, ~loc, ~tailwindFileContent);
Ast_helper.Exp.constant(Pconst_string(classNames, None));
| _ => default_mapper.expr(mapper, e)
};
// Default configuration
let () =
Ppx_config.(
setConfig({tailwindFile: "tailwind.css", rootDirectory: Sys.getcwd()})
);
let args = [
(
"-path",
Arg.String(
tailwindFile =>
Ppx_config.updateConfig(current => {...current, tailwindFile}),
),
"<path/to/tailwind.css>",
),
];
let mapper = (_, _) => {...default_mapper, expr};
let () =
Driver.register(~name="tailwind_ppx", ~args, Versions.ocaml_409, mapper);
================================================
FILE: src/ppx/dune
================================================
(library
(name tailwind_ppx)
(wrapped false)
(public_name tailwind-ppx.lib)
(kind ppx_rewriter)
(libraries str ocaml-migrate-parsetree unix css-parser))
================================================
FILE: src/ppx/levenshtein.re
================================================
/** Levenshtein distance
* Translated from OCaml example in Rosetta Code
* https://rosettacode.org/wiki/Levenshtein_distance#OCaml
* https://en.wikipedia.org/wiki/Levenshtein_distance */
let minimum = (a, b, c) => min(a, min(b, c));
let distance = (s, t) => {
let first = String.length(s)
and second = String.length(t);
let matrix = Array.make_matrix(first + 1, second + 1, 0);
for (i in 0 to first) {
matrix[i][0] = i;
};
for (j in 0 to second) {
matrix[0][j] = j;
};
for (j in 1 to second) {
for (i in 1 to first) {
if (s.[i - 1] == t.[j - 1]) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] =
minimum(
matrix[i - 1][j] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j - 1] + 1,
);
};
};
};
matrix[first][second];
};
================================================
FILE: src/ppx/option.re
================================================
exception Option_unwrap_error;
let map = f =>
fun
| None => None
| Some(v) => Some(f(v));
let flat_map = f =>
fun
| None => None
| Some(v) => f(v);
let unsafe_unwrap =
fun
| None => raise(Option_unwrap_error)
| Some(v) => v;
let get_or_else = default =>
fun
| None => default
| Some(v) => v;
================================================
FILE: src/ppx/ppx_config.re
================================================
type config = {
tailwindFile: string,
rootDirectory: string,
};
let configRef = ref(None);
let setConfig = config => configRef := Some(config);
let updateConfig = update => configRef := configRef^ |> Option.map(update);
let tailwindFile = () => (configRef^ |> Option.unsafe_unwrap).tailwindFile;
let rootDirectory = () => (configRef^ |> Option.unsafe_unwrap).rootDirectory;
================================================
FILE: src/ppx/read_tailwind.re
================================================
/** Recursively look for file starting at the project root */
let rec findFileTowardsRoot = (dir, file) => {
let hereFile = Filename.concat(dir, file);
if (Sys.file_exists(hereFile)) {
Some(hereFile);
} else if (Filename.dirname(dir) == dir) {
None;
} else {
findFileTowardsRoot(Filename.dirname(dir), file);
};
} /* Reads the contents of a file */;
/** Reads the contents of a file */
let readFile = path => {
let ch = open_in(path);
let s =
try(really_input_string(ch, in_channel_length(ch))) {
| End_of_file => ""
};
close_in(ch);
s;
} /* lazily read tailwind and check if talwind.css file exists */;
/** Writes the contents of a file */
let writeFile = (path, newContents) => {
let ch = open_out(path);
output_string(ch, newContents);
close_out(ch);
};
/** lazily read tailwind and check if talwind.css file exists */
let getTailwind =
lazy(
switch (
findFileTowardsRoot(
Ppx_config.rootDirectory(),
Ppx_config.tailwindFile(),
)
) {
| Some(path) => Some(readFile(path))
| None => None
}
);
================================================
FILE: src/ppx/utils.re
================================================
open Css.Types;
module StringSet = Set.Make(String);
/**
* Splits a string on any whitespace into the individual class names
*/
let getSplitClassNames = classNames => {
List.filter(
name => String.trim(name) != "",
Str.split(Str.regexp("[ \n\r\x0c\t]+"), classNames),
)
|> List.map(name => String.trim(name));
};
/** Remove all the backslashes from identifiers */
let unescapeIdent = ident => {
Str.global_replace(Str.regexp({|\\|}), "", ident);
};
type closestClassName = {
name: string,
distance: int,
};
/** Finds the acceptable class name closest to the given invalid one */
let findClosest = (className, acceptableNames) => {
let testCloser = (name, bestMatch) => {
let distance = Levenshtein.distance(className, name);
distance < bestMatch.distance ? {name, distance} : bestMatch;
};
List.fold_right(
testCloser,
acceptableNames,
{name: "", distance: max_int},
);
};
exception ParseError(string);
let parseStylesheet = (~containerLnum=?, ~pos=?, css) =>
try(Css.Parser.parse_stylesheet(~container_lnum=?containerLnum, ~pos?, css)) {
| _ =>
raise(
ParseError(
"Your Tailwind CSS file could not be parsed. Please double-check to make sure it's valid CSS.",
),
)
};
/** Get all the classes from a given selector (prelude) */
let getClassesFromSelector = selector => {
let rec getClasses = (classes, selector) => {
switch (selector) {
| [] => classes
| [
(Component_value.Delim("."), _),
(Component_value.Ident(ident), _),
...rem,
] =>
getClasses([ident, ...classes], rem)
| [_, ...rem] => getClasses(classes, rem)
};
};
getClasses([], selector);
};
/** Parses out the valid class names from the given CSS */
let getAcceptableClassNames = css => {
let (cssRules, _) = parseStylesheet(css);
let rec gatherClassSelector = (existingClassNames, rule) => {
switch (rule) {
| Rule.Style_rule(styleRule) =>
let prelude = fst(styleRule.prelude);
switch (prelude) {
| [
(Component_value.Delim("."), _),
(Component_value.Ident(_), _),
..._,
] =>
List.fold_left(
(classNames, classNameFromSelector) =>
[unescapeIdent(classNameFromSelector), ...classNames],
existingClassNames,
getClassesFromSelector(prelude),
)
| _ => existingClassNames // Ignore other preludes
};
| Rule.At_rule({At_rule.name: ("media", _), At_rule.block, _}) =>
switch (block) {
| Stylesheet((rules, _)) =>
List.fold_left(gatherClassSelector, existingClassNames, rules)
| _ => []
}
// Ignore other prelude
| _ => existingClassNames
};
};
List.fold_left(gatherClassSelector, [], cssRules) |> StringSet.of_list;
};
let checkDuplicate = (classNames, loc) => {
let classNamesSet = ref(StringSet.empty);
let isDuplicate = className => {
StringSet.mem(className, classNamesSet^)
? raise(
Location.Error(
Location.error(~loc, "Duplicate class name: " ++ className),
),
)
: classNamesSet := StringSet.add(className, classNamesSet^);
};
List.iter(isDuplicate, classNames);
};
let checkAcceptable = (classNames, loc, acceptableNames) => {
let errorMessage = invalidClassName => {
let closest =
findClosest(invalidClassName, StringSet.elements(acceptableNames));
Printf.sprintf(
"Class name not found: %s. Did you mean %s?",
invalidClassName,
closest.name,
);
};
let isAcceptable = className => {
StringSet.mem(className, acceptableNames)
? ()
: raise(
Location.Error(Location.error(~loc, errorMessage(className))),
);
};
List.iter(isAcceptable, classNames);
};
exception Ppx_cache_dir_is_not_dir;
// Source: https://github.com/reasonml-community/graphql_ppx/blob/master/src/base/read_schema.re#L301
let createDirIfNotExist = abs_path =>
if (Sys.file_exists(abs_path)) {
let file_stat = Unix.stat(abs_path);
Unix.(
switch (file_stat.st_kind) {
| S_DIR => ()
| _ => raise(Ppx_cache_dir_is_not_dir)
}
);
} else {
switch (Unix.mkdir(abs_path, 493)) {
| () => ()
| exception (Unix.Unix_error(error, cmd, msg)) =>
switch (error) {
| Unix.EEXIST => () /* It's Ok since the build tool e.g. BuckleScript could be multi-threading */
| error => raise(Unix.Unix_error(error, cmd, msg))
}
};
};
type cachedAcceptableClassNames = {
tailwindCssHash: Digest.t,
acceptableClassNames: StringSet.t,
};
let getCachedAcceptableClassNames = (~filePath, ~tailwindFileContent) =>
switch (open_in_bin(filePath)) {
| input =>
let maybeFileContent: option(cachedAcceptableClassNames) =
try(Some(input_value(input))) {
| _ => None
};
close_in(input);
maybeFileContent
|> Option.flat_map(fileContent =>
Digest.equal(
Digest.string(tailwindFileContent),
fileContent.tailwindCssHash,
)
? Some(fileContent.acceptableClassNames) : None
);
| exception _ => None
};
let acceptableClassNames = ref(None);
let validate = (~classNames, ~loc, ~tailwindFileContent) => {
let cacheDirectory =
Filename.concat(Sys.getcwd(), "../../.tailwind_ppx_cache/");
let cacheFilePath =
Filename.concat(cacheDirectory, "active_classnames.marshaled");
let acceptableClassNames =
switch (acceptableClassNames^) {
| Some(value) => value
| None =>
switch (
getCachedAcceptableClassNames(
~filePath=cacheFilePath,
~tailwindFileContent,
)
) {
| Some(cachedAcceptableClassNames) =>
acceptableClassNames := Some(cachedAcceptableClassNames);
cachedAcceptableClassNames;
| None =>
let calculatedAcceptableClassNames =
getAcceptableClassNames(tailwindFileContent);
createDirIfNotExist(cacheDirectory);
let output = open_out_bin(cacheFilePath);
output_value(
output,
{
tailwindCssHash: Digest.string(tailwindFileContent),
acceptableClassNames: calculatedAcceptableClassNames,
},
);
close_out(output);
acceptableClassNames := Some(calculatedAcceptableClassNames);
calculatedAcceptableClassNames;
}
};
let splitClassNames = getSplitClassNames(classNames);
checkAcceptable(splitClassNames, loc, acceptableClassNames);
checkDuplicate(splitClassNames, loc);
};
================================================
FILE: src/use_ppx/Paths.re
================================================
module StringMap = Map.Make(String);
let projectRoot = ref("");
let bsbProjectRoot = ref("");
let bsconfig = "bsconfig.json";
let rec findProjectRoot = (~dir) =>
if (Sys.file_exists(Filename.concat(dir, bsconfig))) {
dir;
} else {
let parent = dir |> Filename.dirname;
if (parent == dir) {
prerr_endline(
"Error: cannot find project root containing " ++ bsconfig ++ ".",
);
assert(false);
} else {
findProjectRoot(~dir=parent);
};
};
let setProjectRoot = () => {
projectRoot := findProjectRoot(~dir=Sys.getcwd());
bsbProjectRoot := projectRoot^;
};
let readDirsFromConfig = (~configSources) => {
let dirs = ref([]);
let root = projectRoot^;
let rec processDir = (~subdirs, dir) => {
let absDir = dir == "" ? root : Filename.concat(root, dir);
if (Sys.file_exists(absDir) && Sys.is_directory(absDir)) {
dirs := [dir, ...dirs^];
if (subdirs) {
absDir
|> Sys.readdir
|> Array.iter(d => processDir(~subdirs, Filename.concat(dir, d)));
};
};
};
let rec processSourceItem = (sourceItem: Ext_json_types.t) =>
switch (sourceItem) {
| Str(str) => str |> processDir(~subdirs=false)
| Obj(map) =>
switch (map |> StringMap.find_opt("dir")) {
| Some(Str(str)) =>
let subdirs =
switch (map |> StringMap.find_opt("subdirs")) {
| Some(True(_)) => true
| Some(False(_)) => false
| _ => false
};
str |> processDir(~subdirs);
| _ => ()
}
| Arr(arr) => arr |> Array.iter(processSourceItem)
| _ => ()
};
switch (configSources) {
| Some(sourceItem) => processSourceItem(sourceItem)
| None => ()
};
dirs^;
};
let readSourceDirs = (~configSources) => {
let sourceDirs =
["lib", "bs", ".sourcedirs.json"]
|> List.fold_left(Filename.concat, bsbProjectRoot^);
let dirs = ref([]);
let readDirs = json => {
switch (json) {
| Ext_json_types.Obj(map) =>
switch (map |> StringMap.find_opt("dirs")) {
| Some(Arr(arr)) =>
arr
|> Array.iter(x =>
switch (x) {
| Ext_json_types.Str(str) => dirs := [str, ...dirs^]
| _ => ()
}
);
();
| _ => ()
}
| _ => ()
};
};
if (sourceDirs |> Sys.file_exists) {
let jsonOpt = sourceDirs |> Ext_json_parse.parse_json_from_file;
switch (jsonOpt) {
| Some(json) =>
if (bsbProjectRoot^ != projectRoot^) {
readDirs(json);
dirs := readDirsFromConfig(~configSources);
} else {
readDirs(json);
}
| None => ()
};
} else {
print_endline("Warning: can't find source dirs " ++ sourceDirs);
dirs := readDirsFromConfig(~configSources);
};
dirs^;
};
================================================
FILE: src/use_ppx/dune
================================================
(copy_files# ext/*.{ml,mli})
(library
(name use_ppx_lib)
(wrapped false)
(libraries str))
================================================
FILE: src/use_ppx/ext/ext_json_parse.ml
================================================
module StringMap = Map.Make (String)
type error =
| Illegal_character of char
| Unterminated_string
| Unterminated_comment
| Illegal_escape of string
| Unexpected_token
| Expect_comma_or_rbracket
| Expect_comma_or_rbrace
| Expect_colon
| Expect_string_or_rbrace
| Expect_eof
(* | Trailing_comma_in_obj *)
(* | Trailing_comma_in_array *)
let fprintf = Format.fprintf
let report_error ppf = function
| Illegal_character c -> fprintf ppf "Illegal character (%s)" (Char.escaped c)
| Illegal_escape s ->
fprintf ppf "Illegal backslash escape in string or character (%s)" s
| Unterminated_string -> fprintf ppf "Unterminated_string"
| Expect_comma_or_rbracket -> fprintf ppf "Expect_comma_or_rbracket"
| Expect_comma_or_rbrace -> fprintf ppf "Expect_comma_or_rbrace"
| Expect_colon -> fprintf ppf "Expect_colon"
| Expect_string_or_rbrace -> fprintf ppf "Expect_string_or_rbrace"
| Expect_eof -> fprintf ppf "Expect_eof"
| Unexpected_token -> fprintf ppf "Unexpected_token"
(* | Trailing_comma_in_obj *)
(* -> fprintf ppf "Trailing_comma_in_obj" *)
(* | Trailing_comma_in_array *)
(* -> fprintf ppf "Trailing_comma_in_array" *)
| Unterminated_comment -> fprintf ppf "Unterminated_comment"
exception Error of Lexing.position * Lexing.position * error
let () =
Printexc.register_printer (function x ->
( match x with
| Error (loc_start, loc_end, error) ->
Some
(Format.asprintf "@[%a:@ %a@ -@ %a)@]" report_error error
Ext_position.print loc_start Ext_position.print loc_end)
| _ -> None ))
type token =
| Comma
| Eof
| False
| Lbrace
| Lbracket
| Null
| Colon
| Number of string
| Rbrace
| Rbracket
| String of string
| True
let error (lexbuf : Lexing.lexbuf) e =
raise (Error (lexbuf.lex_start_p, lexbuf.lex_curr_p, e))
[@@raises Error]
let lexeme_len (x : Lexing.lexbuf) = x.lex_curr_pos - x.lex_start_pos
let update_loc ({ lex_curr_p; _ } as lexbuf : Lexing.lexbuf) diff =
lexbuf.lex_curr_p <-
{
lex_curr_p with
pos_lnum = lex_curr_p.pos_lnum + 1;
pos_bol = lex_curr_p.pos_cnum - diff;
}
let char_for_backslash = function
| 'n' -> '\010'
| 'r' -> '\013'
| 'b' -> '\008'
| 't' -> '\009'
| c -> c
let dec_code c1 c2 c3 =
(100 * (Char.code c1 - 48)) + (10 * (Char.code c2 - 48)) + (Char.code c3 - 48)
let hex_code c1 c2 =
let d1 = Char.code c1 in
let val1 =
if d1 >= 97 then d1 - 87 else if d1 >= 65 then d1 - 55 else d1 - 48
in
let d2 = Char.code c2 in
let val2 =
if d2 >= 97 then d2 - 87 else if d2 >= 65 then d2 - 55 else d2 - 48
in
(val1 * 16) + val2
let lf = '\010'
let __ocaml_lex_tables =
{
Lexing.lex_base =
"\000\000\239\255\240\255\241\255\000\000\025\000\011\000\244\255\245\255\246\255\247\255\248\255\249\255\000\000\000\000\000\000\041\000\001\000\254\255\005\000\005\000\253\255\001\000\002\000\252\255\000\000\000\000\003\000\251\255\001\000\003\000\250\255\079\000\089\000\099\000\121\000\131\000\141\000\153\000\163\000\001\000\253\255\254\255\023\000\255\255\006\000\246\255\189\000\248\255\215\000\255\255\249\255\249\000\181\000\252\255\009\000\063\000\075\000\234\000\251\255\032\001\250\255";
Lexing.lex_backtrk =
"\255\255\255\255\255\255\255\255\013\000\013\000\016\000\255\255\255\255\255\255\255\255\255\255\255\255\016\000\016\000\016\000\016\000\016\000\255\255\000\000\012\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\013\000\255\255\013\000\255\255\013\000\255\255\255\255\255\255\255\255\001\000\255\255\255\255\255\255\008\000\255\255\255\255\255\255\255\255\006\000\006\000\255\255\006\000\001\000\002\000\255\255\255\255\255\255\255\255";
Lexing.lex_default =
"\001\000\000\000\000\000\000\000\255\255\255\255\255\255\000\000\000\000\000\000\000\000\000\000\000\000\255\255\255\255\255\255\255\255\255\255\000\000\255\255\020\000\000\000\255\255\255\255\000\000\255\255\255\255\255\255\000\000\255\255\255\255\000\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\042\000\000\000\000\000\255\255\000\000\047\000\000\000\047\000\000\000\051\000\000\000\000\000\255\255\255\255\000\000\255\255\255\255\255\255\255\255\000\000\255\255\000\000";
Lexing.lex_trans =
"\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\019\000\018\000\018\000\019\000\017\000\019\000\255\255\048\000\019\000\255\255\057\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\019\000\000\000\003\000\000\000\000\000\019\000\000\000\000\000\050\000\000\000\000\000\043\000\008\000\006\000\033\000\016\000\004\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\007\000\004\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\032\000\044\000\033\000\056\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\021\000\057\000\000\000\000\000\000\000\020\000\000\000\000\000\012\000\000\000\011\000\032\000\056\000\000\000\025\000\049\000\000\000\000\000\032\000\014\000\024\000\028\000\000\000\000\000\057\000\026\000\030\000\013\000\031\000\000\000\000\000\022\000\027\000\015\000\029\000\023\000\000\000\000\000\000\000\039\000\010\000\039\000\009\000\032\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\037\000\000\000\037\000\000\000\035\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\255\255\035\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\000\000\000\000\255\255\000\000\056\000\000\000\000\000\055\000\058\000\058\000\058\000\058\000\058\000\058\000\058\000\058\000\058\000\058\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\054\000\000\000\054\000\000\000\000\000\000\000\000\000\054\000\000\000\002\000\041\000\000\000\000\000\000\000\255\255\046\000\053\000\053\000\053\000\053\000\053\000\053\000\053\000\053\000\053\000\053\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\255\255\059\000\059\000\059\000\059\000\059\000\059\000\059\000\059\000\059\000\059\000\000\000\000\000\000\000\000\000\000\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\054\000\000\000\000\000\000\000\000\000\000\000\054\000\060\000\060\000\060\000\060\000\060\000\060\000\000\000\000\000\000\000\000\000\000\000\054\000\000\000\000\000\000\000\054\000\000\000\054\000\000\000\000\000\000\000\052\000\061\000\061\000\061\000\061\000\061\000\061\000\061\000\061\000\061\000\061\000\060\000\060\000\060\000\060\000\060\000\060\000\000\000\061\000\061\000\061\000\061\000\061\000\061\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\061\000\061\000\061\000\061\000\061\000\061\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\255\255\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\255\255\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000";
Lexing.lex_check =
"\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\000\000\000\000\017\000\000\000\000\000\019\000\020\000\045\000\019\000\020\000\055\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\000\000\255\255\000\000\255\255\255\255\019\000\255\255\255\255\045\000\255\255\255\255\040\000\000\000\000\000\004\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\006\000\006\000\006\000\006\000\006\000\006\000\006\000\006\000\006\000\006\000\004\000\043\000\005\000\056\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\005\000\016\000\057\000\255\255\255\255\255\255\016\000\255\255\255\255\000\000\255\255\000\000\005\000\056\000\255\255\014\000\045\000\255\255\255\255\004\000\000\000\023\000\027\000\255\255\255\255\057\000\025\000\029\000\000\000\030\000\255\255\255\255\015\000\026\000\000\000\013\000\022\000\255\255\255\255\255\255\032\000\000\000\032\000\000\000\005\000\032\000\032\000\032\000\032\000\032\000\032\000\032\000\032\000\032\000\032\000\033\000\033\000\033\000\033\000\033\000\033\000\033\000\033\000\033\000\033\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\034\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\035\000\255\255\035\000\255\255\034\000\035\000\035\000\035\000\035\000\035\000\035\000\035\000\035\000\035\000\035\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\036\000\037\000\037\000\037\000\037\000\037\000\037\000\037\000\037\000\037\000\037\000\047\000\034\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\038\000\039\000\039\000\039\000\039\000\039\000\039\000\039\000\039\000\039\000\039\000\255\255\255\255\047\000\255\255\049\000\255\255\255\255\049\000\053\000\053\000\053\000\053\000\053\000\053\000\053\000\053\000\053\000\053\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\049\000\255\255\049\000\255\255\255\255\255\255\255\255\049\000\255\255\000\000\040\000\255\255\255\255\255\255\020\000\045\000\049\000\049\000\049\000\049\000\049\000\049\000\049\000\049\000\049\000\049\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\047\000\058\000\058\000\058\000\058\000\058\000\058\000\058\000\058\000\058\000\058\000\255\255\255\255\255\255\255\255\255\255\052\000\052\000\052\000\052\000\052\000\052\000\052\000\052\000\052\000\052\000\049\000\255\255\255\255\255\255\255\255\255\255\049\000\052\000\052\000\052\000\052\000\052\000\052\000\255\255\255\255\255\255\255\255\255\255\049\000\255\255\255\255\255\255\049\000\255\255\049\000\255\255\255\255\255\255\049\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\052\000\052\000\052\000\052\000\052\000\052\000\255\255\060\000\060\000\060\000\060\000\060\000\060\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\060\000\060\000\060\000\060\000\060\000\060\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\047\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\049\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255";
Lexing.lex_base_code = "";
Lexing.lex_backtrk_code = "";
Lexing.lex_default_code = "";
Lexing.lex_trans_code = "";
Lexing.lex_check_code = "";
Lexing.lex_code = "";
}
let rec lex_json buf lexbuf =
__ocaml_lex_lex_json_rec buf lexbuf 0
[@@raises Error]
and __ocaml_lex_lex_json_rec buf lexbuf __ocaml_lex_state =
match Lexing.engine __ocaml_lex_tables __ocaml_lex_state lexbuf with
| 0 -> lex_json buf lexbuf
| 1 ->
update_loc lexbuf 0;
lex_json buf lexbuf
| 2 -> comment buf lexbuf
| 3 -> True
| 4 -> False
| 5 -> Null
| 6 -> Lbracket
| 7 -> Rbracket
| 8 -> Lbrace
| 9 -> Rbrace
| 10 -> Comma
| 11 -> Colon
| 12 -> lex_json buf lexbuf
| 13 -> Number (Lexing.lexeme lexbuf)
| 14 ->
let pos = Lexing.lexeme_start_p lexbuf in
scan_string buf pos lexbuf;
let content = Buffer.contents buf in
Buffer.clear buf;
String content
| 15 -> Eof
| 16 ->
let c = Lexing.sub_lexeme_char lexbuf lexbuf.Lexing.lex_start_pos in
error lexbuf (Illegal_character c)
| __ocaml_lex_state ->
lexbuf.Lexing.refill_buff lexbuf;
__ocaml_lex_lex_json_rec buf lexbuf __ocaml_lex_state
[@@raises Error]
and comment buf lexbuf = __ocaml_lex_comment_rec buf lexbuf 40 [@@raises Error]
and __ocaml_lex_comment_rec buf lexbuf __ocaml_lex_state =
match Lexing.engine __ocaml_lex_tables __ocaml_lex_state lexbuf with
| 0 -> lex_json buf lexbuf
| 1 -> comment buf lexbuf
| 2 -> error lexbuf Unterminated_comment
| __ocaml_lex_state ->
lexbuf.Lexing.refill_buff lexbuf;
__ocaml_lex_comment_rec buf lexbuf __ocaml_lex_state
[@@raises Error]
and scan_string buf start lexbuf =
__ocaml_lex_scan_string_rec buf start lexbuf 45
[@@raises Error]
and __ocaml_lex_scan_string_rec buf start lexbuf __ocaml_lex_state =
match Lexing.engine __ocaml_lex_tables __ocaml_lex_state lexbuf with
| 0 -> ()
| 1 ->
let len = lexeme_len lexbuf - 2 in
update_loc lexbuf len;
scan_string buf start lexbuf
| 2 ->
let len = lexeme_len lexbuf - 3 in
update_loc lexbuf len;
scan_string buf start lexbuf
| 3 ->
let c = Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 1) in
Buffer.add_char buf (char_for_backslash c);
scan_string buf start lexbuf
| 4 ->
let c1 = Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 1)
and c2 = Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 2)
and c3 = Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 3)
and s =
Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_start_pos
(lexbuf.Lexing.lex_start_pos + 4)
in
let v = dec_code c1 c2 c3 in
( try Buffer.add_char buf (Char.chr v)
with _ -> error lexbuf (Illegal_escape s) );
scan_string buf start lexbuf
| 5 ->
let c1 = Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 2)
and c2 =
Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 3)
in
let v = hex_code c1 c2 in
( try Buffer.add_char buf (Char.chr v)
with _ -> error lexbuf (Illegal_escape (Char.escaped c2)) );
scan_string buf start lexbuf
| 6 ->
let c = Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 1) in
Buffer.add_char buf '\\';
Buffer.add_char buf c;
scan_string buf start lexbuf
| 7 ->
update_loc lexbuf 0;
Buffer.add_char buf lf;
scan_string buf start lexbuf
| 8 ->
let ofs = lexbuf.lex_start_pos in
let len = lexbuf.lex_curr_pos - ofs in
Buffer.add_substring buf (Bytes.to_string lexbuf.lex_buffer) ofs len;
scan_string buf start lexbuf
| 9 -> error lexbuf Unterminated_string
| __ocaml_lex_state ->
lexbuf.Lexing.refill_buff lexbuf;
__ocaml_lex_scan_string_rec buf start lexbuf __ocaml_lex_state
[@@raises Error]
let parse_json lexbuf =
let buf = Buffer.create 64 in
let look_ahead = ref None in
let token () : token =
match !look_ahead with
| None -> lex_json buf lexbuf
| Some x ->
look_ahead := None;
x
[@@raises Error]
in
let push e = look_ahead := Some e in
let rec json (lexbuf : Lexing.lexbuf) : Ext_json_types.t =
match token () with
| True -> True lexbuf.lex_start_p
| False -> False lexbuf.lex_start_p
| Null -> Null lexbuf.lex_start_p
| Number s -> Flo s
| String s -> Str s
| Lbracket -> parse_array lexbuf.lex_start_p lexbuf.lex_curr_p [] lexbuf
| Lbrace -> parse_map lexbuf.lex_start_p StringMap.empty lexbuf
| _ -> error lexbuf Unexpected_token
(* Note if we remove [trailing_comma] support
we should report errors (actually more work), for example
{[
match token () with
| Rbracket ->
if trailing_comma then
error lexbuf Trailing_comma_in_array
else
]}
{[
match token () with
| Rbrace ->
if trailing_comma then
error lexbuf Trailing_comma_in_obj
else
]}
*)
[@@raises Error]
and parse_array loc_start loc_finish acc lexbuf : Ext_json_types.t =
match token () with
| Rbracket -> Arr (Array.of_list (acc |> List.rev))
| x -> (
push x;
let new_one = json lexbuf in
match token () with
| Comma -> parse_array loc_start loc_finish (new_one :: acc) lexbuf
| Rbracket -> Arr (Array.of_list (new_one :: acc |> List.rev))
| _ -> error lexbuf Expect_comma_or_rbracket )
[@@raises Error]
and parse_map loc_start acc lexbuf : Ext_json_types.t =
match token () with
| Rbrace -> Obj acc
| String key -> (
match token () with
| Colon -> (
let value = json lexbuf in
match token () with
| Rbrace -> Obj (StringMap.add key value acc)
| Comma -> parse_map loc_start (StringMap.add key value acc) lexbuf
| _ -> error lexbuf Expect_comma_or_rbrace )
| _ -> error lexbuf Expect_colon )
| _ -> error lexbuf Expect_string_or_rbrace
[@@raises Error]
in
let v = json lexbuf in
match token () with Eof -> v | _ -> error lexbuf Expect_eof
[@@raises Error]
let parse_json_from_file s =
let in_chan = open_in s in
match
let lexbuf = Ext_position.lexbuf_from_channel_with_fname in_chan s in
parse_json lexbuf
with
| exception (Error _ | Invalid_argument _ | Sys_error _) ->
close_in_noerr in_chan;
None
| v ->
close_in_noerr in_chan;
Some v
================================================
FILE: src/use_ppx/ext/ext_json_types.ml
================================================
(* Copyright (C) 2015-2017 Bloomberg Finance L.P.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition to the permissions granted to you by the LGPL, you may combine
* or link a "work that uses the Library" with a publicly distributed version
* of this file to produce a combined library or application, then distribute
* that combined work under the terms of your choosing, with no requirement
* to comply with the obligations normally placed on you by section 4 of the
* LGPL version 3 (or the corresponding section of a later version of the LGPL
* should you choose to use a later version).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
type loc = Lexing.position
type json_str = string
type json_flo = string
module StringMap = Map.Make (String)
type json_array = t array
and json_map = t StringMap.t
and t =
| True of loc
| False of loc
| Null of loc
| Flo of json_flo
| Str of json_str
| Arr of json_array
| Obj of json_map
================================================
FILE: src/use_ppx/ext/ext_position.ml
================================================
(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition to the permissions granted to you by the LGPL, you may combine
* or link a "work that uses the Library" with a publicly distributed version
* of this file to produce a combined library or application, then distribute
* that combined work under the terms of your choosing, with no requirement
* to comply with the obligations normally placed on you by section 4 of the
* LGPL version 3 (or the corresponding section of a later version of the LGPL
* should you choose to use a later version).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *)
type t = Lexing.position = {
pos_fname : string; [@dead]
pos_lnum : int;
pos_bol : int;
pos_cnum : int;
}
let print fmt (pos : t) =
Format.fprintf fmt "(line %d, column %d)" pos.pos_lnum
(pos.pos_cnum - pos.pos_bol)
let lexbuf_from_channel_with_fname ic fname =
let x = Lexing.from_function (fun buf n -> input ic buf 0 n) in
let pos : t =
{
pos_fname = fname;
pos_lnum = 1;
pos_bol = 0;
pos_cnum = 0 (* copied from zero_pos*);
}
in
x.lex_start_p <- pos;
x.lex_curr_p <- pos;
x
[@@raises Invalid_argument]
================================================
FILE: tailwind-ppx.opam
================================================
# This file is generated by dune, edit dune-project instead
opam-version: "2.0"
synopsis: "A short, but powerful statement about your project"
description: """
A short, but powerful statement about your project
"""
maintainer: ["Dylan Irlbeck"]
authors: ["Dylan Irlbeck"]
license: "MIT"
homepage: "https://github.com/dylanirlbeck/tailwind-ppx"
bug-reports: "https://github.com/dylanirlbeck/tailwind-ppx/issues"
depends: [
"ocaml" {>= "4.06.0"}
"dune" {>= "2.0"}
"odoc" {with-doc}
"reason" {build}
"base" {build}
"ppxlib" {build}
]
dev-repo: "git+https://github.com/dylanirlbeck/tailwind-ppx.git"
# We need to avoid "@runtest", since it depends on rely
build: [
["dune" "subst"] {pinned}
[
"dune"
"build"
"-p"
name
"-j"
jobs
"@install"
"@doc" {with-doc}
]
]
================================================
FILE: tailwind-ppx.opam.template
================================================
# We need to avoid "@runtest", since it depends on rely
build: [
["dune" "subst"] {pinned}
[
"dune"
"build"
"-p"
name
"-j"
jobs
"@install"
"@doc" {with-doc}
]
]
================================================
FILE: test/bucklescript/.gitignore
================================================
.merlin
.bsb.lock
*.log
/lib/bs/
node_modules
/build/
**/*.bs.js
dist/
esy.lock
.cache
_esy
.tailwind_ppx_cache
================================================
FILE: test/bucklescript/bsconfig.json
================================================
{
"name": "bucklescript",
"reason": {
"react-jsx": 3
},
"sources": {
"dir": "src",
"subdirs": true,
"type": "dev"
},
"package-specs": [
{
"module": "commonjs",
"in-source": true
}
],
"ppx-flags": [["../../_build/default/bin/Bin.exe", "-path ../tailwind.css"]],
"suffix": ".bs.js",
"namespace": true,
"bs-dependencies": ["reason-react"],
"bs-dev-dependencies": ["@glennsl/bs-jest", "bs-react-testing-library"],
"refmt": 3
}
================================================
FILE: test/bucklescript/dune
================================================
(dirs :standard \ node_modules)
================================================
FILE: test/bucklescript/index.css
================================================
@tailwind base;
@tailwind components;
@tailwind utilities;
================================================
FILE: test/bucklescript/package.json
================================================
{
"name": "bucklescript",
"version": "0.0.0",
"scripts": {
"build:styles": "tailwind build index.css -o ../tailwind.css -c tailwind.config.js",
"build": "bsb -make-world",
"watch": "bsb -make-world -w",
"clean": "bsb -clean-world",
"test": "jest",
"start": "yarn test"
},
"dependencies": {
"@glennsl/bs-jest": "^0.4.10",
"bs-platform": "^8.3.3",
"bs-react-testing-library": "^0.8.0",
"jest": "^25.1.0",
"minimist": "^1.2.3",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"reason-react": "^0.7.0"
},
"jest": {
"testMatch": [
"**/src/**/*_test.bs.js"
]
},
"devDependencies": {
"tailwindcss": "^1.4.1"
}
}
================================================
FILE: test/bucklescript/src/Demo.re
================================================
[@react.component]
let make = () => {
<div
className=[%tw
"flex flex-col items-center justify-center w-full h-full bg-white"
]>
<span> "Content"->React.string </span>
</div>;
};
================================================
FILE: test/bucklescript/src/index_res_test.res
================================================
open Jest
open Expect
@bs.module("../../../js/index")
external extractor: string => array<string> = "extractor"
describe("rescript tests", () => {
describe("Basic test", () => {
test("basic", () => {
let className = %tw("flex-row flex")
expect(className) |> toBe("flex-row flex")
})
test("should ignore spaces arround", () => {
// This should not cause compilation errors
let className = %tw(" flex-row flex ")
// And we receive the same string in the output AST
expect(className) |> toBe(" flex-row flex ")
})
test("Responsive utilities should work", () => {
let className = %tw("sm:text-5xl")
expect(className) |> toBe("sm:text-5xl")
})
})
describe("Extractor", () => {
test(`it extracts the contents of %tw("..") tags properly`, () => {
expect(
extractor(
`
<!-- This should be included -->
<div className=%tw("flex flex-col bg-gray-200 hover:bg-gray-200")></div>
<!-- These shouldn't be included -->
<div className="flex-row mx-auto mt-2"></div>
<!-- ...but these should -->
<div className=%tw("mb-2 p-6")></div> <div className=%tw("mt-8 bg-blue-200")></div>
<!-- ...and these -->
<div
className={
switch (test) {
| A =>
%tw(
"bg-red-400"
)
| B =>
%tw(
"bg-green-400"
)
}
}
/>
<!-- ...and so should these -->
<div
className=%tw(
"bg-blue-400"
)
/>
`,
),
) |> toEqual([
"flex",
"flex-col",
"bg-gray-200",
"hover:bg-gray-200",
"mb-2",
"p-6",
"mt-8",
"bg-blue-200",
"bg-red-400",
"bg-green-400",
"bg-blue-400",
])
})
})
})
================================================
FILE: test/bucklescript/src/index_test.re
================================================
open Jest;
open Expect;
[@bs.module "../../../js/index"]
external extractor: string => array(string) = "extractor";
describe("Basic test", () => {
test("basic", () => {
let className = [%tw "flex-row flex"];
expect(className) |> toBe("flex-row flex");
});
test("should ignore spaces arround", () => {
// This should not cause compilation errors
let className = [%tw " flex-row flex "];
// And we receive the same string in the output AST
expect(className) |> toBe(" flex-row flex ");
});
test("Responsive utilities should work", () => {
let className = [%tw "sm:text-5xl"];
expect(className) |> toBe("sm:text-5xl");
});
});
describe("Extractor", () => {
test("it extracts the content of [%tw] tags properly", () => {
expect(
extractor(
{|
<!-- This should be included -->
<div className=[%tw "flex flex-col bg-gray-200 hover:bg-gray-200"]></div>
<!-- These shouldn't be included -->
<div className="flex-row mx-auto mt-2"></div>
<!-- ...but these should -->
<div className=[%tw "mb-2 p-6"]></div> <div className=[%tw "mt-8 bg-blue-200"]></div>
<!-- ...and these -->
<div
className={
switch (test) {
| A =>
%tw
"bg-red-400"
| B =>
%tw
"bg-green-400"
}
}
/>
<!-- ...and so should these -->
<div
className=[%tw
"bg-blue-400"
]
/>
|},
),
)
|> toEqual([|
"flex",
"flex-col",
"bg-gray-200",
"hover:bg-gray-200",
"mb-2",
"p-6",
"mt-8",
"bg-blue-200",
"bg-red-400",
"bg-green-400",
"bg-blue-400",
|])
})
});
================================================
FILE: test/bucklescript/tailwind.config.js
================================================
module.exports = {
important: true,
theme: {},
extend: {},
variants: {
display: ["responsive", "hover", "focus", "group-hover"],
boxShadow: ["focus", "active"],
backgroundColor: ["hover", "focus", "active", "focus-within"],
borderColor: ["hover", "focus", "active", "focus-within"],
text: ["hover", "focus", "active", "group-hover", "focus-within"],
margin: ["last"],
borderRadius: ["last", "first"],
borderWidth: ["last", "first", "odd", "even"]
}
};
================================================
FILE: test/bucklescript/tailwind_fake.css
================================================
.flex {
display: flex;
}
.flex-row {
flex-direction: row;
}
================================================
FILE: test/native/TestRunner.re
================================================
TailwindPpxTestNativeLib.Setup.cli();
================================================
FILE: test/native/dune
================================================
(executable
(name TestRunner)
(public_name TailwindPpxTestRunner)
(libraries
TailwindPpxTestNativeLib
)
)
================================================
FILE: test/native/dune-project
================================================
(lang dune 1.6)
================================================
FILE: test/native/lib/Setup.re
================================================
include Rely.Make({
let config =
Rely.TestFrameworkConfig.initialize({
snapshotDir: "test/native/lib/__snapshots",
projectDir: "test/native",
});
});
================================================
FILE: test/native/lib/Test.re
================================================
open Setup;
open Utils;
open Css.Types;
open Helpers;
describe("Main methods", ({test, _}) => {
test("Parser works for base tailwind file", ({expect, _}) => {
let baseTailwindCss = Read_tailwind.readFile("test/tailwind.css");
parseStylesheet(baseTailwindCss) |> ignore;
// TODO find a better way to make sure an error isn't thrown
expect.bool(true).toBeTrue();
});
test("Parser handles @-webkit-keyframes", ({expect, _}) => {
let css = {|
@-webkit-keyframes hide {
from {
opacity: 0.8;
visibility: visible;
}
to {
opacity: 0;
height: 0;
visibility: hidden;
}
}
|};
parseStylesheet(css) |> ignore;
expect.bool(true).toBeTrue();
});
test("splitClassNames works with whitespace", ({expect, _}) => {
let className = "flex flex-row ";
expect.list(getSplitClassNames(className)).toEqual([
"flex",
"flex-row",
]);
});
test("splitClassNames works with whitespace", ({expect, _}) => {
let className = "w-full h-full bg-white flex flex-col justify-center \n
items-center";
expect.list(getSplitClassNames(className)).toEqual([
"w-full",
"h-full",
"bg-white",
"flex",
"flex-col",
"justify-center",
"items-center",
]);
});
test("splitClassNames works with whitespace", ({expect, _}) => {
let className = "justify-center\nitems-center";
expect.list(getSplitClassNames(className)).toEqual([
"justify-center",
"items-center",
]);
});
test("checkDuplicate throws correctly", ({expect, _}) => {
let classNames = ["flex", "flex"];
expect.fn(() => checkDuplicate(classNames, Location.none)).toThrow();
});
test("levenshtein distance", ({expect, _}) => {
let acceptableNames = ["flex", "flex-row", "hover:bg-mono-100"];
let {name, _} = findClosest("bg-mono-100", acceptableNames);
expect.string(name).toEqual("hover:bg-mono-100");
});
test("parseStylesheet throws correctly", ({expect, _}) => {
let tailwindCss = {|
.flex {
display: flex;
}
.flex-row {
flex-direction: row;
--asdfa
}
|};
expect.fn(() => parseStylesheet(tailwindCss)).toThrowException(
ParseError(
"Your Tailwind CSS file could not be parsed. Please double-check to make sure it's valid CSS.",
),
);
});
describe("parseStylesheet works for different selectors", ({test, _}) => {
test("Basic selectors", ({expect, _}) => {
let tailwindCss = {|
.flex {
display: flex;
}
.flex-row {
flex-direction: row;
}
|};
let ast = parseStylesheet(tailwindCss);
let expected_ast = (
[
Rule.Style_rule({
Style_rule.prelude: (
[
(Component_value.Delim("."), Location.none),
(Component_value.Ident("flex"), Location.none),
],
Location.none,
),
block: (
[
Declaration_list.Declaration({
Declaration.name: ("display", Location.none),
value: (
[(Component_value.Ident("flex"), Location.none)],
Location.none,
),
important: (false, Location.none),
loc: Location.none,
}),
],
Location.none,
),
loc: Location.none,
}),
Rule.Style_rule({
Style_rule.prelude: (
[
(Component_value.Delim("."), Location.none),
(Component_value.Ident("flex-row"), Location.none),
],
Location.none,
),
block: (
[
Declaration_list.Declaration({
Declaration.name: ("flex-direction", Location.none),
value: (
[(Component_value.Ident("row"), Location.none)],
Location.none,
),
important: (false, Location.none),
loc: Location.none,
}),
],
Location.none,
),
loc: Location.none,
}),
],
Location.none,
);
expect.bool(eq_ast(ast, expected_ast)).toBeTrue();
});
test("Hover selector", ({expect, _}) => {
let tailwindCss = {|
.hover\:bg-mono-100:hover {
background-color: #FFF;
}
|};
let ast = parseStylesheet(tailwindCss);
let expected_ast = (
[
Rule.Style_rule({
Style_rule.prelude: (
[
(Component_value.Delim("."), Location.none),
(
Component_value.Ident("hover\\:bg-mono-100"),
Location.none,
),
(Component_value.Delim(":"), Location.none),
(Component_value.Ident("hover"), Location.none),
],
Location.none,
),
block: (
[
Declaration_list.Declaration({
Declaration.name: ("background-color", Location.none),
value: (
[(Component_value.Hash("FFF"), Location.none)],
Location.none,
),
important: (false, Location.none),
loc: Location.none,
}),
],
Location.none,
),
loc: Location.none,
}),
],
Location.none,
);
expect.bool(eq_ast(ast, expected_ast)).toBeTrue();
});
});
});
describe(
"getClassesFromSelector gets all classes in a given selector", ({test, _}) => {
test("Basic selectors", ({expect, _}) => {
let flex = [
(Component_value.Delim("."), Location.none),
(Component_value.Ident("flex"), Location.none),
];
let expectedClassNames = ["flex"];
expect.list(getClassesFromSelector(flex)).toEqual(expectedClassNames);
});
test("Hover selector", ({expect, _}) => {
let hover = [
(Component_value.Delim("."), Location.none),
(Component_value.Ident("hover\\:bg-white"), Location.none),
(Component_value.Delim(":"), Location.none),
(Component_value.Ident("hover"), Location.none),
];
let expectedClassNames = ["hover\\:bg-white"];
expect.list(getClassesFromSelector(hover)).toEqual(expectedClassNames);
});
test("Multiple classnames with different pseudo classes", ({expect, _}) => {
let hover = [
(Component_value.Delim("."), Location.none),
(Component_value.Ident("group"), Location.none),
(Component_value.Delim(":"), Location.none),
(Component_value.Ident("hover"), Location.none),
(Component_value.Delim("."), Location.none),
(Component_value.Ident("group-hover\\:bg-transparent"), Location.none),
];
let expectedClassNames = ["group-hover\\:bg-transparent", "group"];
expect.list(getClassesFromSelector(hover)).toEqual(expectedClassNames);
});
});
describe(
"getAcceptableClassNames works for different CSS selectors", ({test, _}) => {
test("Basic selectors", ({expect, _}) => {
let tailwindCss = {|
.flex {
display: flex;
}
.flex-row {
flex-direction: row;
}
|};
let expectedClassNames = ["flex", "flex-row"];
expect.list(getAcceptableClassNames(tailwindCss) |> StringSet.elements).
toEqual(
expectedClassNames,
);
});
test("Hover selector", ({expect, _}) => {
let tailwindCss = {|
.hover\:bg-mono-100:hover {
background-color: #FFF;
}
|};
let expectedClassNames = ["hover:bg-mono-100"];
expect.list(getAcceptableClassNames(tailwindCss) |> StringSet.elements).
toEqual(
expectedClassNames,
);
});
test("Responsive utilities", ({expect, _}) => {
let tailwindCss = {|
@media (min-width: 640px) {
.sm\:text-5xl {
font-size: 3rem;
}
}
|};
let expectedClassNames = ["sm:text-5xl"];
expect.list(getAcceptableClassNames(tailwindCss) |> StringSet.elements).
toEqual(
expectedClassNames,
);
});
test("Group hover", ({expect, _}) => {
let tailwindCss = {|
.group:hover .group-hover\:bg-transparent {
background-color: transparent !imporant;
}
|};
let expectedClassNames = ["group", "group-hover:bg-transparent"];
expect.list(getAcceptableClassNames(tailwindCss) |> StringSet.elements).
toEqual(
expectedClassNames,
);
});
});
================================================
FILE: test/native/lib/dune
================================================
(library
(name TailwindPpxTestNativeLib)
(library_flags (-linkall -g))
(libraries fmt rely.lib tailwind-ppx.lib)
)
================================================
FILE: test/native/lib/helpers.ml
================================================
open Css.Types
let rec zip xs ys =
match (xs, ys) with
| [], _ -> []
| _, [] -> []
| x :: xs, y :: ys -> (x, y) :: zip xs ys
let eq_ast ast1 ast2 =
let eq_list xs ys eq =
List.length xs = List.length ys
&& List.fold_left (fun e (x, y) -> e && eq x y) true (zip xs ys)
in
let rec eq_component_value (cv1, _) (cv2, _) =
let open Component_value in
match (cv1, cv2) with
| Paren_block b1, Paren_block b2 | Bracket_block b1, Bracket_block b2 ->
eq_list b1 b2 eq_component_value
| Percentage x1, Percentage x2
| Ident x1, Ident x2
| String x1, String x2
| Uri x1, Uri x2
| Operator x1, Operator x2
| Delim x1, Delim x2
| Hash x1, Hash x2
| Number x1, Number x2
| Unicode_range x1, Unicode_range x2 ->
x1 = x2
| Float_dimension x1, Float_dimension x2 -> x1 = x2
| Dimension x1, Dimension x2 -> x1 = x2
| Function ((n1, _), (b1, _)), Function ((n2, _), (b2, _)) ->
n1 = n2 && eq_list b1 b2 eq_component_value
| _ -> false
and eq_at_rule r1 r2 =
let n1, _ = r1.At_rule.name in
let n2, _ = r2.At_rule.name in
let pr1, _ = r1.At_rule.prelude in
let pr2, _ = r2.At_rule.prelude in
n1 = n2
&& eq_list pr1 pr2 eq_component_value
&&
match (r1.At_rule.block, r2.At_rule.block) with
| Brace_block.Empty, Brace_block.Empty -> true
| Brace_block.Declaration_list dl1, Brace_block.Declaration_list dl2 ->
eq_declaration_list dl1 dl2
| Brace_block.Stylesheet s1, Brace_block.Stylesheet s2 ->
eq_stylesheet s1 s2
| _ -> false
and eq_declaration d1 d2 =
let n1, _ = d1.Declaration.name in
let n2, _ = d2.Declaration.name in
let v1, _ = d1.Declaration.value in
let v2, _ = d2.Declaration.value in
let i1, _ = d1.Declaration.important in
let i2, _ = d2.Declaration.important in
n1 = n2 && eq_list v1 v2 eq_component_value && i1 = i2
and eq_declaration_list (dl1, _) (dl2, _) =
let eq_kind k1 k2 =
match (k1, k2) with
| Declaration_list.Declaration d1, Declaration_list.Declaration d2 ->
eq_declaration d1 d2
| Declaration_list.At_rule r1, Declaration_list.At_rule r2 ->
eq_at_rule r1 r2
| _ -> false
in
eq_list dl1 dl2 eq_kind
and eq_style_rule r1 r2 =
let pr1, _ = r1.Style_rule.prelude in
let pr2, _ = r2.Style_rule.prelude in
eq_list pr1 pr2 eq_component_value
&& eq_declaration_list r1.Style_rule.block r2.Style_rule.block
and eq_rule r1 r2 =
match (r1, r2) with
| Rule.Style_rule r1, Rule.Style_rule r2 -> eq_style_rule r1 r2
| Rule.At_rule r1, Rule.At_rule r2 -> eq_at_rule r1 r2
| _ -> false
and eq_stylesheet (st1, _) (st2, _) = eq_list st1 st2 eq_rule in
eq_stylesheet ast1 ast2
================================================
FILE: test/native/tailwind-ppx-test-native.opam
================================================
================================================
FILE: test/tailwind.css
================================================
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
-webkit-text-decoration: underline dotted;
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}
/**
* Manually forked from SUIT CSS Base: https://github.com/suitcss/base
* A thin layer on top of normalize.css that provides a starting point more
* suitable for web applications.
*/
/**
* Removes the default spacing and border for appropriate elements.
*/
blockquote,
dl,
dd,
h1,
h2,
h3,
h4,
h5,
h6,
hr,
figure,
p,
pre {
margin: 0;
}
button {
background-color: transparent;
background-image: none;
padding: 0;
}
/**
* Work around a Firefox/IE bug where the transparent `button` background
* results in a loss of the default `button` focus styles.
*/
button:focus {
outline: 1px dotted;
outline: 5px auto -webkit-focus-ring-color;
}
fieldset {
margin: 0;
padding: 0;
}
ol,
ul {
list-style: none;
margin: 0;
padding: 0;
}
/**
* Tailwind custom reset styles
*/
/**
* 1. Use the user's configured `sans` font-family (with Tailwind's default
* sans-serif font stack as a fallback) as a sane default.
* 2. Use Tailwind's default "normal" line-height so the user isn't forced
* to override it to ensure consistency even when using the default theme.
*/
html {
font-family: 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"; /* 1 */
line-height: 1.5; /* 2 */
}
/**
* 1. Prevent padding and border from affecting element width.
*
* We used to set this in the html element and inherit from
* the parent element for everything else. This caused issues
* in shadow-dom-enhanced elements like <details> where the content
* is wrapped by a div with box-sizing set to `content-box`.
*
* https://github.com/mozdevs/cssremedy/issues/4
*
*
* 2. Allow adding a border to an element by just adding a border-width.
*
* By default, the way the browser specifies that an element should have no
* border is by setting it's border-style to `none` in the user-agent
* stylesheet.
*
* In order to easily add borders to elements by just setting the `border-width`
* property, we change the default border-style for all elements to `solid`, and
* use border-width to hide them instead. This way our `border` utilities only
* need to set the `border-width` property instead of the entire `border`
* shorthand, making our border utilities much more straightforward to compose.
*
* https://github.com/tailwindcss/tailwindcss/pull/116
*/
*,
::before,
::after {
box-sizing: border-box; /* 1 */
border-width: 0; /* 2 */
border-style: solid; /* 2 */
border-color: #e2e8f0; /* 2 */
}
/*
* Ensure horizontal rules are visible by default
*/
hr {
border-top-width: 1px;
}
/**
* Undo the `border-style: none` reset that Normalize applies to images so that
* our `border-{width}` utilities have the expected effect.
*
* The Normalize reset is unnecessary for us since we default the border-width
* to 0 on all elements.
*
* https://github.com/tailwindcss/tailwindcss/issues/362
*/
img {
border-style: solid;
}
textarea {
resize: vertical;
}
input::-webkit-input-placeholder, textarea::-webkit-input-placeholder {
color: #a0aec0;
}
input::-moz-placeholder, textarea::-moz-placeholder {
color: #a0aec0;
}
input:-ms-input-placeholder, textarea:-ms-input-placeholder {
color: #a0aec0;
}
input::-ms-input-placeholder, textarea::-ms-input-placeholder {
color: #a0aec0;
}
input::placeholder,
textarea::placeholder {
color: #a0aec0;
}
button,
[role="button"] {
cursor: pointer;
}
table {
border-collapse: collapse;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: inherit;
font-weight: inherit;
}
/**
* Reset links to optimize for opt-in styling instead of
* opt-out.
*/
a {
color: inherit;
text-decoration: inherit;
}
/**
* Reset form element properties that are easy to forget to
* style explicitly so you don't inadvertently introduce
* styles that deviate from your design system. These styles
* supplement a partial reset that is already applied by
* normalize.css.
*/
button,
input,
optgroup,
select,
textarea {
padding: 0;
line-height: inherit;
color: inherit;
}
/**
* Use the configured 'mono' font family for elements that
* are expected to be rendered with a monospace font, falling
* back to the system monospace stack if there is no configured
* 'mono' font family.
*/
pre,
code,
kbd,
samp {
font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
/**
* Make replaced elements `display: block` by default as that's
* the behavior you want almost all of the time. Inspired by
* CSS Remedy, with `svg` added as well.
*
* https://github.com/mozdevs/cssremedy/issues/14
*/
img,
svg,
video,
canvas,
audio,
iframe,
embed,
object {
display: block;
vertical-align: middle;
}
/**
* Constrain images and videos to the parent width and preserve
* their instrinsic aspect ratio.
*
* https://github.com/mozdevs/cssremedy/issues/14
*/
img,
video {
max-width: 100%;
height: auto;
}
.container {
width: 100%;
}
@media (min-width: 640px) {
.container {
max-width: 640px;
}
}
@media (min-width: 768px) {
.container {
max-width: 768px;
}
}
@media (min-width: 1024px) {
.container {
max-width: 1024px;
}
}
@media (min-width: 1280px) {
.container {
max-width: 1280px;
}
}
.space-y-0 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(0px * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(0px * var(--space-y-reverse)) !important;
}
.space-x-0 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(0px * var(--space-x-reverse)) !important;
margin-left: calc(0px * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-1 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(0.25rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(0.25rem * var(--space-y-reverse)) !important;
}
.space-x-1 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(0.25rem * var(--space-x-reverse)) !important;
margin-left: calc(0.25rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-2 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(0.5rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(0.5rem * var(--space-y-reverse)) !important;
}
.space-x-2 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(0.5rem * var(--space-x-reverse)) !important;
margin-left: calc(0.5rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-3 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(0.75rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(0.75rem * var(--space-y-reverse)) !important;
}
.space-x-3 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(0.75rem * var(--space-x-reverse)) !important;
margin-left: calc(0.75rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-4 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(1rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(1rem * var(--space-y-reverse)) !important;
}
.space-x-4 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(1rem * var(--space-x-reverse)) !important;
margin-left: calc(1rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-5 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(1.25rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(1.25rem * var(--space-y-reverse)) !important;
}
.space-x-5 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(1.25rem * var(--space-x-reverse)) !important;
margin-left: calc(1.25rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-6 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(1.5rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(1.5rem * var(--space-y-reverse)) !important;
}
.space-x-6 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(1.5rem * var(--space-x-reverse)) !important;
margin-left: calc(1.5rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-8 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(2rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(2rem * var(--space-y-reverse)) !important;
}
.space-x-8 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(2rem * var(--space-x-reverse)) !important;
margin-left: calc(2rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-10 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(2.5rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(2.5rem * var(--space-y-reverse)) !important;
}
.space-x-10 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(2.5rem * var(--space-x-reverse)) !important;
margin-left: calc(2.5rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-12 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(3rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(3rem * var(--space-y-reverse)) !important;
}
.space-x-12 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(3rem * var(--space-x-reverse)) !important;
margin-left: calc(3rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-16 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(4rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(4rem * var(--space-y-reverse)) !important;
}
.space-x-16 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(4rem * var(--space-x-reverse)) !important;
margin-left: calc(4rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-20 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(5rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(5rem * var(--space-y-reverse)) !important;
}
.space-x-20 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(5rem * var(--space-x-reverse)) !important;
margin-left: calc(5rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-24 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(6rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(6rem * var(--space-y-reverse)) !important;
}
.space-x-24 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(6rem * var(--space-x-reverse)) !important;
margin-left: calc(6rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-32 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(8rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(8rem * var(--space-y-reverse)) !important;
}
.space-x-32 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(8rem * var(--space-x-reverse)) !important;
margin-left: calc(8rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-40 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(10rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(10rem * var(--space-y-reverse)) !important;
}
.space-x-40 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(10rem * var(--space-x-reverse)) !important;
margin-left: calc(10rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-48 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(12rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(12rem * var(--space-y-reverse)) !important;
}
.space-x-48 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(12rem * var(--space-x-reverse)) !important;
margin-left: calc(12rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-56 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(14rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(14rem * var(--space-y-reverse)) !important;
}
.space-x-56 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(14rem * var(--space-x-reverse)) !important;
margin-left: calc(14rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-64 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(16rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(16rem * var(--space-y-reverse)) !important;
}
.space-x-64 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(16rem * var(--space-x-reverse)) !important;
margin-left: calc(16rem * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-px > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(1px * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(1px * var(--space-y-reverse)) !important;
}
.space-x-px > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(1px * var(--space-x-reverse)) !important;
margin-left: calc(1px * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-1 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-0.25rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-0.25rem * var(--space-y-reverse)) !important;
}
.-space-x-1 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-0.25rem * var(--space-x-reverse)) !important;
margin-left: calc(-0.25rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-2 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-0.5rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-0.5rem * var(--space-y-reverse)) !important;
}
.-space-x-2 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-0.5rem * var(--space-x-reverse)) !important;
margin-left: calc(-0.5rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-3 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-0.75rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-0.75rem * var(--space-y-reverse)) !important;
}
.-space-x-3 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-0.75rem * var(--space-x-reverse)) !important;
margin-left: calc(-0.75rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-4 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-1rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-1rem * var(--space-y-reverse)) !important;
}
.-space-x-4 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-1rem * var(--space-x-reverse)) !important;
margin-left: calc(-1rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-5 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-1.25rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-1.25rem * var(--space-y-reverse)) !important;
}
.-space-x-5 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-1.25rem * var(--space-x-reverse)) !important;
margin-left: calc(-1.25rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-6 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-1.5rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-1.5rem * var(--space-y-reverse)) !important;
}
.-space-x-6 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-1.5rem * var(--space-x-reverse)) !important;
margin-left: calc(-1.5rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-8 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-2rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-2rem * var(--space-y-reverse)) !important;
}
.-space-x-8 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-2rem * var(--space-x-reverse)) !important;
margin-left: calc(-2rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-10 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-2.5rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-2.5rem * var(--space-y-reverse)) !important;
}
.-space-x-10 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-2.5rem * var(--space-x-reverse)) !important;
margin-left: calc(-2.5rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-12 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-3rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-3rem * var(--space-y-reverse)) !important;
}
.-space-x-12 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-3rem * var(--space-x-reverse)) !important;
margin-left: calc(-3rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-16 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-4rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-4rem * var(--space-y-reverse)) !important;
}
.-space-x-16 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-4rem * var(--space-x-reverse)) !important;
margin-left: calc(-4rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-20 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-5rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-5rem * var(--space-y-reverse)) !important;
}
.-space-x-20 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-5rem * var(--space-x-reverse)) !important;
margin-left: calc(-5rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-24 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-6rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-6rem * var(--space-y-reverse)) !important;
}
.-space-x-24 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-6rem * var(--space-x-reverse)) !important;
margin-left: calc(-6rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-32 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-8rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-8rem * var(--space-y-reverse)) !important;
}
.-space-x-32 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-8rem * var(--space-x-reverse)) !important;
margin-left: calc(-8rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-40 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-10rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-10rem * var(--space-y-reverse)) !important;
}
.-space-x-40 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-10rem * var(--space-x-reverse)) !important;
margin-left: calc(-10rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-48 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-12rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-12rem * var(--space-y-reverse)) !important;
}
.-space-x-48 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-12rem * var(--space-x-reverse)) !important;
margin-left: calc(-12rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-56 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-14rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-14rem * var(--space-y-reverse)) !important;
}
.-space-x-56 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-14rem * var(--space-x-reverse)) !important;
margin-left: calc(-14rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-64 > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-16rem * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-16rem * var(--space-y-reverse)) !important;
}
.-space-x-64 > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-16rem * var(--space-x-reverse)) !important;
margin-left: calc(-16rem * calc(1 - var(--space-x-reverse))) !important;
}
.-space-y-px > :not(template) ~ :not(template) {
--space-y-reverse: 0 !important;
margin-top: calc(-1px * calc(1 - var(--space-y-reverse))) !important;
margin-bottom: calc(-1px * var(--space-y-reverse)) !important;
}
.-space-x-px > :not(template) ~ :not(template) {
--space-x-reverse: 0 !important;
margin-right: calc(-1px * var(--space-x-reverse)) !important;
margin-left: calc(-1px * calc(1 - var(--space-x-reverse))) !important;
}
.space-y-reverse > :not(template) ~ :not(template) {
--space-y-reverse: 1 !important;
}
.space-x-reverse > :not(template) ~ :not(template) {
--space-x-reverse: 1 !important;
}
.divide-y-0 > :not(template) ~ :not(template) {
--divide-y-reverse: 0 !important;
border-top-width: calc(0px * calc(1 - var(--divide-y-reverse))) !important;
border-bottom-width: calc(0px * var(--divide-y-reverse)) !important;
}
.divide-x-0 > :not(template) ~ :not(template) {
--divide-x-reverse: 0 !important;
border-right-width: calc(0px * var(--divide-x-reverse)) !important;
border-left-width: calc(0px * calc(1 - var(--divide-x-reverse))) !important;
}
.divide-y-2 > :not(template) ~ :not(template) {
--divide-y-reverse: 0 !important;
border-top-width: calc(2px * calc(1 - var(--divide-y-reverse))) !important;
border-bottom-width: calc(2px * var(--divide-y-reverse)) !important;
}
.divide-x-2 > :not(template) ~ :not(template) {
--divide-x-reverse: 0 !important;
border-right-width: calc(2px * var(--divide-x-reverse)) !important;
border-left-width: calc(2px * calc(1 - var(--divide-x-reverse))) !important;
}
.divide-y-4 > :not(template) ~ :not(template) {
--divide-y-reverse: 0 !important;
border-top-width: calc(4px * calc(1 - var(--divide-y-reverse))) !important;
border-bottom-width: calc(4px * var(--divide-y-reverse)) !important;
}
.divide-x-4 > :not(template) ~ :not(template) {
--divide-x-reverse: 0 !important;
border-right-width: calc(4px * var(--divide-x-reverse)) !important;
border-left-width: calc(4px * calc(1 - var(--divide-x-reverse))) !important;
}
.divide-y-8 > :not(template) ~ :not(template) {
--divide-y-reverse: 0 !important;
border-top-width: calc(8px * calc(1 - var(--divide-y-reverse))) !important;
border-bottom-width: calc(8px * var(--divide-y-reverse)) !important;
}
.divide-x-8 > :not(template) ~ :not(template) {
--divide-x-reverse: 0 !important;
border-right-width: calc(8px * var(--divide-x-reverse)) !important;
border-left-width: calc(8px * calc(1 - var(--divide-x-reverse))) !important;
}
.divide-y > :not(template) ~ :not(template) {
--divide-y-reverse: 0 !important;
border-top-width: calc(1px * calc(1 - var(--divide-y-reverse))) !important;
border-bottom-width: calc(1px * var(--divide-y-reverse)) !important;
}
.divide-x > :not(template) ~ :not(template) {
--divide-x-reverse: 0 !important;
border-right-width: calc(1px * var(--divide-x-reverse)) !important;
border-left-width: calc(1px * calc(1 - var(--divide-x-reverse))) !important;
}
.divide-y-reverse > :not(template) ~ :not(template) {
--divide-y-reverse: 1 !important;
}
.divide-x-reverse > :not(template) ~ :not(template) {
--divide-x-reverse: 1 !important;
}
.divide-transparent > :not(template) ~ :not(template) {
border-color: transparent !important;
}
.divide-current > :not(template) ~ :not(template) {
border-color: currentColor !important;
}
.divide-black > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #000 !important;
border-color: rgba(0, 0, 0, var(--divide-opacity)) !important;
}
.divide-white > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fff !important;
border-color: rgba(255, 255, 255, var(--divide-opacity)) !important;
}
.divide-gray-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #f7fafc !important;
border-color: rgba(247, 250, 252, var(--divide-opacity)) !important;
}
.divide-gray-200 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #edf2f7 !important;
border-color: rgba(237, 242, 247, var(--divide-opacity)) !important;
}
.divide-gray-300 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #e2e8f0 !important;
border-color: rgba(226, 232, 240, var(--divide-opacity)) !important;
}
.divide-gray-400 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #cbd5e0 !important;
border-color: rgba(203, 213, 224, var(--divide-opacity)) !important;
}
.divide-gray-500 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #a0aec0 !important;
border-color: rgba(160, 174, 192, var(--divide-opacity)) !important;
}
.divide-gray-600 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #718096 !important;
border-color: rgba(113, 128, 150, var(--divide-opacity)) !important;
}
.divide-gray-700 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #4a5568 !important;
border-color: rgba(74, 85, 104, var(--divide-opacity)) !important;
}
.divide-gray-800 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #2d3748 !important;
border-color: rgba(45, 55, 72, var(--divide-opacity)) !important;
}
.divide-gray-900 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #1a202c !important;
border-color: rgba(26, 32, 44, var(--divide-opacity)) !important;
}
.divide-red-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fff5f5 !important;
border-color: rgba(255, 245, 245, var(--divide-opacity)) !important;
}
.divide-red-200 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fed7d7 !important;
border-color: rgba(254, 215, 215, var(--divide-opacity)) !important;
}
.divide-red-300 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #feb2b2 !important;
border-color: rgba(254, 178, 178, var(--divide-opacity)) !important;
}
.divide-red-400 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fc8181 !important;
border-color: rgba(252, 129, 129, var(--divide-opacity)) !important;
}
.divide-red-500 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #f56565 !important;
border-color: rgba(245, 101, 101, var(--divide-opacity)) !important;
}
.divide-red-600 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #e53e3e !important;
border-color: rgba(229, 62, 62, var(--divide-opacity)) !important;
}
.divide-red-700 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #c53030 !important;
border-color: rgba(197, 48, 48, var(--divide-opacity)) !important;
}
.divide-red-800 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #9b2c2c !important;
border-color: rgba(155, 44, 44, var(--divide-opacity)) !important;
}
.divide-red-900 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #742a2a !important;
border-color: rgba(116, 42, 42, var(--divide-opacity)) !important;
}
.divide-orange-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fffaf0 !important;
border-color: rgba(255, 250, 240, var(--divide-opacity)) !important;
}
.divide-orange-200 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #feebc8 !important;
border-color: rgba(254, 235, 200, var(--divide-opacity)) !important;
}
.divide-orange-300 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fbd38d !important;
border-color: rgba(251, 211, 141, var(--divide-opacity)) !important;
}
.divide-orange-400 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #f6ad55 !important;
border-color: rgba(246, 173, 85, var(--divide-opacity)) !important;
}
.divide-orange-500 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #ed8936 !important;
border-color: rgba(237, 137, 54, var(--divide-opacity)) !important;
}
.divide-orange-600 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #dd6b20 !important;
border-color: rgba(221, 107, 32, var(--divide-opacity)) !important;
}
.divide-orange-700 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #c05621 !important;
border-color: rgba(192, 86, 33, var(--divide-opacity)) !important;
}
.divide-orange-800 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #9c4221 !important;
border-color: rgba(156, 66, 33, var(--divide-opacity)) !important;
}
.divide-orange-900 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #7b341e !important;
border-color: rgba(123, 52, 30, var(--divide-opacity)) !important;
}
.divide-yellow-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fffff0 !important;
border-color: rgba(255, 255, 240, var(--divide-opacity)) !important;
}
.divide-yellow-200 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fefcbf !important;
border-color: rgba(254, 252, 191, var(--divide-opacity)) !important;
}
.divide-yellow-300 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #faf089 !important;
border-color: rgba(250, 240, 137, var(--divide-opacity)) !important;
}
.divide-yellow-400 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #f6e05e !important;
border-color: rgba(246, 224, 94, var(--divide-opacity)) !important;
}
.divide-yellow-500 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #ecc94b !important;
border-color: rgba(236, 201, 75, var(--divide-opacity)) !important;
}
.divide-yellow-600 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #d69e2e !important;
border-color: rgba(214, 158, 46, var(--divide-opacity)) !important;
}
.divide-yellow-700 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #b7791f !important;
border-color: rgba(183, 121, 31, var(--divide-opacity)) !important;
}
.divide-yellow-800 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #975a16 !important;
border-color: rgba(151, 90, 22, var(--divide-opacity)) !important;
}
.divide-yellow-900 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #744210 !important;
border-color: rgba(116, 66, 16, var(--divide-opacity)) !important;
}
.divide-green-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #f0fff4 !important;
border-color: rgba(240, 255, 244, var(--divide-opacity)) !important;
}
.divide-green-200 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #c6f6d5 !important;
border-color: rgba(198, 246, 213, var(--divide-opacity)) !important;
}
.divide-green-300 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #9ae6b4 !important;
border-color: rgba(154, 230, 180, var(--divide-opacity)) !important;
}
.divide-green-400 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #68d391 !important;
border-color: rgba(104, 211, 145, var(--divide-opacity)) !important;
}
.divide-green-500 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #48bb78 !important;
border-color: rgba(72, 187, 120, var(--divide-opacity)) !important;
}
.divide-green-600 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #38a169 !important;
border-color: rgba(56, 161, 105, var(--divide-opacity)) !important;
}
.divide-green-700 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #2f855a !important;
border-color: rgba(47, 133, 90, var(--divide-opacity)) !important;
}
.divide-green-800 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #276749 !important;
border-color: rgba(39, 103, 73, var(--divide-opacity)) !important;
}
.divide-green-900 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #22543d !important;
border-color: rgba(34, 84, 61, var(--divide-opacity)) !important;
}
.divide-teal-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #e6fffa !important;
border-color: rgba(230, 255, 250, var(--divide-opacity)) !important;
}
.divide-teal-200 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #b2f5ea !important;
border-color: rgba(178, 245, 234, var(--divide-opacity)) !important;
}
.divide-teal-300 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #81e6d9 !important;
border-color: rgba(129, 230, 217, var(--divide-opacity)) !important;
}
.divide-teal-400 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #4fd1c5 !important;
border-color: rgba(79, 209, 197, var(--divide-opacity)) !important;
}
.divide-teal-500 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #38b2ac !important;
border-color: rgba(56, 178, 172, var(--divide-opacity)) !important;
}
.divide-teal-600 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #319795 !important;
border-color: rgba(49, 151, 149, var(--divide-opacity)) !important;
}
.divide-teal-700 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #2c7a7b !important;
border-color: rgba(44, 122, 123, var(--divide-opacity)) !important;
}
.divide-teal-800 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #285e61 !important;
border-color: rgba(40, 94, 97, var(--divide-opacity)) !important;
}
.divide-teal-900 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #234e52 !important;
border-color: rgba(35, 78, 82, var(--divide-opacity)) !important;
}
.divide-blue-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #ebf8ff !important;
border-color: rgba(235, 248, 255, var(--divide-opacity)) !important;
}
.divide-blue-200 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #bee3f8 !important;
border-color: rgba(190, 227, 248, var(--divide-opacity)) !important;
}
.divide-blue-300 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #90cdf4 !important;
border-color: rgba(144, 205, 244, var(--divide-opacity)) !important;
}
.divide-blue-400 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #63b3ed !important;
border-color: rgba(99, 179, 237, var(--divide-opacity)) !important;
}
.divide-blue-500 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #4299e1 !important;
border-color: rgba(66, 153, 225, var(--divide-opacity)) !important;
}
.divide-blue-600 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #3182ce !important;
border-color: rgba(49, 130, 206, var(--divide-opacity)) !important;
}
.divide-blue-700 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #2b6cb0 !important;
border-color: rgba(43, 108, 176, var(--divide-opacity)) !important;
}
.divide-blue-800 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #2c5282 !important;
border-color: rgba(44, 82, 130, var(--divide-opacity)) !important;
}
.divide-blue-900 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #2a4365 !important;
border-color: rgba(42, 67, 101, var(--divide-opacity)) !important;
}
.divide-indigo-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #ebf4ff !important;
border-color: rgba(235, 244, 255, var(--divide-opacity)) !important;
}
.divide-indigo-200 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #c3dafe !important;
border-color: rgba(195, 218, 254, var(--divide-opacity)) !important;
}
.divide-indigo-300 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #a3bffa !important;
border-color: rgba(163, 191, 250, var(--divide-opacity)) !important;
}
.divide-indigo-400 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #7f9cf5 !important;
border-color: rgba(127, 156, 245, var(--divide-opacity)) !important;
}
.divide-indigo-500 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #667eea !important;
border-color: rgba(102, 126, 234, var(--divide-opacity)) !important;
}
.divide-indigo-600 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #5a67d8 !important;
border-color: rgba(90, 103, 216, var(--divide-opacity)) !important;
}
.divide-indigo-700 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #4c51bf !important;
border-color: rgba(76, 81, 191, var(--divide-opacity)) !important;
}
.divide-indigo-800 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #434190 !important;
border-color: rgba(67, 65, 144, var(--divide-opacity)) !important;
}
.divide-indigo-900 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #3c366b !important;
border-color: rgba(60, 54, 107, var(--divide-opacity)) !important;
}
.divide-purple-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #faf5ff !important;
border-color: rgba(250, 245, 255, var(--divide-opacity)) !important;
}
.divide-purple-200 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #e9d8fd !important;
border-color: rgba(233, 216, 253, var(--divide-opacity)) !important;
}
.divide-purple-300 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #d6bcfa !important;
border-color: rgba(214, 188, 250, var(--divide-opacity)) !important;
}
.divide-purple-400 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #b794f4 !important;
border-color: rgba(183, 148, 244, var(--divide-opacity)) !important;
}
.divide-purple-500 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #9f7aea !important;
border-color: rgba(159, 122, 234, var(--divide-opacity)) !important;
}
.divide-purple-600 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #805ad5 !important;
border-color: rgba(128, 90, 213, var(--divide-opacity)) !important;
}
.divide-purple-700 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #6b46c1 !important;
border-color: rgba(107, 70, 193, var(--divide-opacity)) !important;
}
.divide-purple-800 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #553c9a !important;
border-color: rgba(85, 60, 154, var(--divide-opacity)) !important;
}
.divide-purple-900 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #44337a !important;
border-color: rgba(68, 51, 122, var(--divide-opacity)) !important;
}
.divide-pink-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fff5f7 !important;
border-color: rgba(255, 245, 247, var(--divide-opacity)) !important;
}
.divide-pink-200 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fed7e2 !important;
border-color: rgba(254, 215, 226, var(--divide-opacity)) !important;
}
.divide-pink-300 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #fbb6ce !important;
border-color: rgba(251, 182, 206, var(--divide-opacity)) !important;
}
.divide-pink-400 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #f687b3 !important;
border-color: rgba(246, 135, 179, var(--divide-opacity)) !important;
}
.divide-pink-500 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #ed64a6 !important;
border-color: rgba(237, 100, 166, var(--divide-opacity)) !important;
}
.divide-pink-600 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #d53f8c !important;
border-color: rgba(213, 63, 140, var(--divide-opacity)) !important;
}
.divide-pink-700 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #b83280 !important;
border-color: rgba(184, 50, 128, var(--divide-opacity)) !important;
}
.divide-pink-800 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #97266d !important;
border-color: rgba(151, 38, 109, var(--divide-opacity)) !important;
}
.divide-pink-900 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
border-color: #702459 !important;
border-color: rgba(112, 36, 89, var(--divide-opacity)) !important;
}
.divide-opacity-0 > :not(template) ~ :not(template) {
--divide-opacity: 0 !important;
}
.divide-opacity-25 > :not(template) ~ :not(template) {
--divide-opacity: 0.25 !important;
}
.divide-opacity-50 > :not(template) ~ :not(template) {
--divide-opacity: 0.5 !important;
}
.divide-opacity-75 > :not(template) ~ :not(template) {
--divide-opacity: 0.75 !important;
}
.divide-opacity-100 > :not(template) ~ :not(template) {
--divide-opacity: 1 !important;
}
.sr-only {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
border-width: 0 !important;
}
.not-sr-only {
position: static !important;
width: auto !important;
height: auto !important;
padding: 0 !important;
margin: 0 !important;
overflow: visible !important;
clip: auto !important;
white-space: normal !important;
}
.focus\:sr-only:focus {
position: absolute !important;
width: 1px !important;
height: 1px !important;
padding: 0 !important;
margin: -1px !important;
overflow: hidden !important;
clip: rect(0, 0, 0, 0) !important;
white-space: nowrap !important;
border-width: 0 !important;
}
.focus\:not-sr-only:focus {
position: static !important;
width: auto !important;
height: auto !important;
padding: 0 !important;
margin: 0 !important;
overflow: visible !important;
clip: auto !important;
white-space: normal !important;
}
.appearance-none {
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
}
.bg-fixed {
background-attachment: fixed !important;
}
.bg-local {
background-attachment: local !important;
}
.bg-scroll {
background-attachment: scroll !important;
}
.bg-transparent {
background-color: transparent !important;
}
.bg-current {
background-color: currentColor !important;
}
.bg-black {
--bg-opacity: 1 !important;
background-color: #000 !important;
background-color: rgba(0, 0, 0, var(--bg-opacity)) !important;
}
.bg-white {
--bg-opacity: 1 !important;
background-color: #fff !important;
background-color: rgba(255, 255, 255, var(--bg-opacity)) !important;
}
.bg-gray-100 {
--bg-opacity: 1 !important;
background-color: #f7fafc !important;
background-color: rgba(247, 250, 252, var(--bg-opacity)) !important;
}
.bg-gray-200 {
--bg-opacity: 1 !important;
background-color: #edf2f7 !important;
background-color: rgba(237, 242, 247, var(--bg-opacity)) !important;
}
.bg-gray-300 {
--bg-opacity: 1 !important;
background-color: #e2e8f0 !important;
background-color: rgba(226, 232, 240, var(--bg-opacity)) !important;
}
.bg-gray-400 {
--bg-opacity: 1 !important;
background-color: #cbd5e0 !important;
background-color: rgba(203, 213, 224, var(--bg-opacity)) !important;
}
.bg-gray-500 {
--bg-opacity: 1 !important;
background-color: #a0aec0 !important;
background-color: rgba(160, 174, 192, var(--bg-opacity)) !important;
}
.bg-gray-600 {
--bg-opacity: 1 !important;
background-color: #718096 !important;
background-color: rgba(113, 128, 150, var(--bg-opacity)) !important;
}
.bg-gray-700 {
--bg-opacity: 1 !important;
background-color: #4a5568 !important;
background-color: rgba(74, 85, 104, var(--bg-opacity)) !important;
}
.bg-gray-800 {
--bg-opacity: 1 !important;
background-color: #2d3748 !important;
background-color: rgba(45, 55, 72, var(--bg-opacity)) !important;
}
.bg-gray-900 {
--bg-opacity: 1 !important;
background-color: #1a202c !important;
background-color: rgba(26, 32, 44, var(--bg-opacity)) !important;
}
.bg-red-100 {
--bg-opacity: 1 !important;
background-color: #fff5f5 !important;
background-color: rgba(255, 245, 245, var(--bg-opacity)) !important;
}
.bg-red-200 {
--bg-opacity: 1 !important;
background-color: #fed7d7 !important;
background-color: rgba(254, 215, 215, var(--bg-opacity)) !important;
}
.bg-red-300 {
--bg-opacity: 1 !important;
background-color: #feb2b2 !important;
background-color: rgba(254, 178, 178, var(--bg-opacity)) !important;
}
.bg-red-400 {
--bg-opacity: 1 !important;
background-color: #fc8181 !important;
background-color: rgba(252, 129, 129, var(--bg-opacity)) !important;
}
.bg-red-500 {
--bg-opacity: 1 !important;
background-color: #f56565 !important;
background-color: rgba(245, 101, 101, var(--bg-opacity)) !important;
}
.bg-red-600 {
--bg-opacity: 1 !important;
background-color: #e53e3e !important;
background-color: rgba(229, 62, 62, var(--bg-opacity)) !important;
}
.bg-red-700 {
--bg-opacity: 1 !important;
background-color: #c53030 !important;
background-color: rgba(197, 48, 48, var(--bg-opacity)) !important;
}
.bg-red-800 {
--bg-opacity: 1 !important;
background-color: #9b2c2c !important;
background-color: rgba(155, 44, 44, var(--bg-opacity)) !important;
}
.bg-red-900 {
--bg-opacity: 1 !important;
background-color: #742a2a !important;
background-color: rgba(116, 42, 42, var(--bg-opacity)) !important;
}
.bg-orange-100 {
--bg-opacity: 1 !important;
background-color: #fffaf0 !important;
background-color: rgba(255, 250, 240, var(--bg-opacity)) !important;
}
.bg-orange-200 {
--bg-opacity: 1 !important;
background-color: #feebc8 !important;
background-color: rgba(254, 235, 200, var(--bg-opacity)) !important;
}
.bg-orange-300 {
--bg-opacity: 1 !important;
background-color: #fbd38d !important;
background-color: rgba(251, 211, 141, var(--bg-opacity)) !important;
}
.bg-orange-400 {
--bg-opacity: 1 !important;
background-color: #f6ad55 !important;
background-color: rgba(246, 173, 85, var(--bg-opacity)) !important;
}
.bg-orange-500 {
--bg-opacity: 1 !important;
background-color: #ed8936 !important;
background-color: rgba(237, 137, 54, var(--bg-opacity)) !important;
}
.bg-orange-600 {
--bg-opacity: 1 !important;
background-color: #dd6b20 !important;
background-color: rgba(221, 107, 32, var(--bg-opacity)) !important;
}
.bg-orange-700 {
--bg-opacity: 1 !important;
background-color: #c05621 !important;
background-color: rgba(192, 86, 33, var(--bg-opacity)) !important;
}
.bg-orange-800 {
--bg-opacity: 1 !important;
background-color: #9c4221 !important;
background-color: rgba(156, 66, 33, var(--bg-opacity)) !important;
}
.bg-orange-900 {
--bg-opacity: 1 !important;
background-color: #7b341e !important;
background-color: rgba(123, 52, 30, var(--bg-opacity)) !important;
}
.bg-yellow-100 {
--bg-opacity: 1 !important;
background-color: #fffff0 !important;
background-color: rgba(255, 255, 240, var(--bg-opacity)) !important;
}
.bg-yellow-200 {
--bg-opacity: 1 !important;
background-color: #fefcbf !important;
background-color: rgba(254, 252, 191, var(--bg-opacity)) !important;
}
.bg-yellow-300 {
--bg-opacity: 1 !important;
background-color: #faf089 !important;
background-color: rgba(250, 240, 137, var(--bg-opacity)) !important;
}
.bg-yellow-400 {
--bg-opacity: 1 !important;
background-color: #f6e05e !important;
background-color: rgba(246, 224, 94, var(--bg-opacity)) !important;
}
.bg-yellow-500 {
--bg-opacity: 1 !important;
background-color: #ecc94b !important;
background-color: rgba(236, 201, 75, var(--bg-opacity)) !important;
}
.bg-yellow-600 {
--bg-opacity: 1 !important;
background-color: #d69e2e !important;
background-color: rgba(214, 158, 46, var(--bg-opacity)) !important;
}
.bg-yellow-700 {
--bg-opacity: 1 !important;
background-color: #b7791f !important;
background-color: rgba(183, 121, 31, var(--bg-opacity)) !important;
}
.bg-yellow-800 {
--bg-opacity: 1 !important;
background-color: #975a16 !important;
background-color: rgba(151, 90, 22, var(--bg-opacity)) !important;
}
.bg-yellow-900 {
--bg-opacity: 1 !important;
background-color: #744210 !important;
background-color: rgba(116, 66, 16, var(--bg-opacity)) !important;
}
.bg-green-100 {
--bg-opacity: 1 !important;
background-color: #f0fff4 !important;
background-color: rgba(240, 255, 244, var(--bg-opacity)) !important;
}
.bg-green-200 {
--bg-opacity: 1 !important;
background-color: #c6f6d5 !important;
background-color: rgba(198, 246, 213, var(--bg-opacity)) !important;
}
.bg-green-300 {
--bg-opacity: 1 !important;
background-color: #9ae6b4 !important;
background-color: rgba(154, 230, 180, var(--bg-opacity)) !important;
}
.bg-green-400 {
--bg-opacity: 1 !important;
background-color: #68d391 !important;
background-color: rgba(104, 211, 145, var(--bg-opacity)) !important;
}
.bg-green-500 {
--bg-opacity: 1 !important;
background-color: #48bb78 !important;
background-color: rgba(72, 187, 120, var(--bg-opacity)) !important;
}
.bg-green-600 {
--bg-opacity: 1 !important;
background-color: #38a169 !important;
background-color: rgba(56, 161, 105, var(--bg-opacity)) !important;
}
.bg-green-700 {
--bg-opacity: 1 !important;
background-color: #2f855a !important;
background-color: rgba(47, 133, 90, var(--bg-opacity)) !important;
}
.bg-green-800 {
--bg-opacity: 1 !important;
background-color: #276749 !important;
background-color: rgba(39, 103, 73, var(--bg-opacity)) !important;
}
.bg-green-900 {
--bg-opacity: 1 !important;
background-color: #22543d !important;
background-color: rgba(34, 84, 61, var(--bg-opacity)) !important;
}
.bg-teal-100 {
--bg-opacity: 1 !important;
background-color: #e6fffa !important;
background-color: rgba(230, 255, 250, var(--bg-opacity)) !important;
}
.bg-teal-200 {
--bg-opacity: 1 !important;
background-color: #b2f5ea !important;
background-color: rgba(178, 245, 234, var(--bg-opacity)) !important;
}
.bg-teal-300 {
--bg-opacity: 1 !important;
background-color: #81e6d9 !important;
background-color: rgba(129, 230, 217, var(--bg-opacity)) !important;
}
.bg-teal-400 {
--bg-opacity: 1 !important;
background-color: #4fd1c5 !important;
background-color: rgba(79, 209, 197, var(--bg-opacity)) !important;
}
.bg-teal-500 {
--bg-opacity: 1 !important;
background-color: #38b2ac !important;
background-color: rgba(56, 178, 172, var(--bg-opacity)) !important;
}
.bg-teal-600 {
--bg-opacity: 1 !important;
background-color: #319795 !important;
background-color: rgba(49, 151, 149, var(--bg-opacity)) !important;
}
.bg-teal-700 {
--bg-opacity: 1 !important;
background-color: #2c7a7b !important;
background-color: rgba(44, 122, 123, var(--bg-opacity)) !important;
}
.bg-teal-800 {
--bg-opacity: 1 !important;
background-color: #285e61 !important;
background-color: rgba(40, 94, 97, var(--bg-opacity)) !important;
}
.bg-teal-900 {
--bg-opacity: 1 !important;
background-color: #234e52 !important;
background-color: rgba(35, 78, 82, var(--bg-opacity)) !important;
}
.bg-blue-100 {
--bg-opacity: 1 !important;
background-color: #ebf8ff !important;
background-color: rgba(235, 248, 255, var(--bg-opacity)) !important;
}
.bg-blue-200 {
--bg-opacity: 1 !important;
background-color: #bee3f8 !important;
background-color: rgba(190, 227, 248, var(--bg-opacity)) !important;
}
.bg-blue-300 {
--bg-opacity: 1 !important;
background-color: #90cdf4 !important;
background-color: rgba(144, 205, 244, var(--bg-opacity)) !important;
}
.bg-blue-400 {
--bg-opacity: 1 !important;
background-color: #63b3ed !important;
background-color: rgba(99, 179, 237, var(--bg-opacity)) !important;
}
.bg-blue-500 {
--bg-opacity: 1 !important;
background-color: #4299e1 !important;
background-color: rgba(66, 153, 225, var(--bg-opacity)) !important;
}
.bg-blue-600 {
--bg-opacity: 1 !important;
background-color: #3182ce !important;
background-color: rgba(49, 130, 206, var(--bg-opacity)) !important;
}
.bg-blue-700 {
--bg-opacity: 1 !important;
background-color: #2b6cb0 !important;
background-color: rgba(43, 108, 176, var(--bg-opacity)) !important;
}
.bg-blue-800 {
--bg-opacity: 1 !important;
background-color: #2c5282 !important;
background-color: rgba(44, 82, 130, var(--bg-opacity)) !important;
}
.bg-blue-900 {
--bg-opacity: 1 !important;
background-color: #2a4365 !important;
background-color: rgba(42, 67, 101, var(--bg-opacity)) !important;
}
.bg-indigo-100 {
--bg-opacity: 1 !important;
background-color: #ebf4ff !important;
background-color: rgba(235, 244, 255, var(--bg-opacity)) !important;
}
.bg-indigo-200 {
--bg-opacity: 1 !important;
background-color: #c3dafe !important;
background-color: rgba(195, 218, 254, var(--bg-opacity)) !important;
}
.bg-indigo-300 {
--bg-opacity: 1 !important;
background-color: #a3bffa !important;
background-color: rgba(163, 191, 250, var(--bg-opacity)) !important;
}
.bg-indigo-400 {
--bg-opacity: 1 !important;
background-color: #7f9cf5 !important;
background-color: rgba(127, 156, 245, var(--bg-opacity)) !important;
}
.bg-indigo-500 {
--bg-opacity: 1 !important;
background-color: #667eea !important;
background-color: rgba(102, 126, 234, var(--bg-opacity)) !important;
}
.bg-indigo-600 {
--bg-opacity: 1 !important;
background-color: #5a67d8 !important;
background-color: rgba(90, 103, 216, var(--bg-opacity)) !important;
}
.bg-indigo-700 {
--bg-opacity: 1 !important;
background-color: #4c51bf !important;
background-color: rgba(76, 81, 191, var(--bg-opacity)) !important;
}
.bg-indigo-800 {
--bg-opacity: 1 !important;
background-color: #434190 !important;
background-color: rgba(67, 65, 144, var(--bg-opacity)) !important;
}
.bg-indigo-900 {
--bg-opacity: 1 !important;
background-color: #3c366b !important;
background-color: rgba(60, 54, 107, var(--bg-opacity)) !important;
}
.bg-purple-100 {
--bg-opacity: 1 !important;
background-color: #faf5ff !important;
background-color: rgba(250, 245, 255, var(--bg-opacity)) !important;
}
.bg-purple-200 {
--bg-opacity: 1 !important;
background-color: #e9d8fd !important;
background-color: rgba(233, 216, 253, var(--bg-opacity)) !important;
}
.bg-purple-300 {
--bg-opacity: 1 !important;
background-color: #d6bcfa !important;
background-color: rgba(214, 188, 250, var(--bg-opacity)) !important;
}
.bg-purple-400 {
--bg-opacity: 1 !important;
background-color: #b794f4 !important;
background-color: rgba(183, 148, 244, var(--bg-opacity)) !important;
}
.bg-purple-500 {
--bg-opacity: 1 !important;
background-color: #9f7aea !important;
background-color: rgba(159, 122, 234, var(--bg-opacity)) !important;
}
.bg-purple-600 {
--bg-opacity: 1 !important;
background-color: #805ad5 !important;
background-color: rgba(128, 90, 213, var(--bg-opacity)) !important;
}
.bg-purple-700 {
--bg-opacity: 1 !important;
background-color: #6b46c1 !important;
background-color: rgba(107, 70, 193, var(--bg-opacity)) !important;
}
.bg-purple-800 {
--bg-opacity: 1 !important;
background-color: #553c9a !important;
background-color: rgba(85, 60, 154, var(--bg-opacity)) !important;
}
.bg-purple-900 {
--bg-opacity: 1 !important;
background-color: #44337a !important;
background-color: rgba(68, 51, 122, var(--bg-opacity)) !important;
}
.bg-pink-100 {
--bg-opacity: 1 !important;
background-color: #fff5f7 !important;
background-color: rgba(255, 245, 247, var(--bg-opacity)) !important;
}
.bg-pink-200 {
--bg-opacity: 1 !important;
background-color: #fed7e2 !important;
background-color: rgba(254, 215, 226, var(--bg-opacity)) !important;
}
.bg-pink-300 {
--bg-opacity: 1 !important;
background-color: #fbb6ce !important;
background-color: rgba(251, 182, 206, var(--bg-opacity)) !important;
}
.bg-pink-400 {
--bg-opacity: 1 !important;
background-color: #f687b3 !important;
background-color: rgba(246, 135, 179, var(--bg-opacity)) !important;
}
.bg-pink-500 {
--bg-opacity: 1 !important;
background-color: #ed64a6 !important;
background-color: rgba(237, 100, 166, var(--bg-opacity)) !important;
}
.bg-pink-600 {
--bg-opacity: 1 !important;
background-color: #d53f8c !important;
background-color: rgba(213, 63, 140, var(--bg-opacity)) !important;
}
.bg-pink-700 {
--bg-opacity: 1 !important;
background-color: #b83280 !important;
background-color: rgba(184, 50, 128, var(--bg-opacity)) !important;
}
.bg-pink-800 {
--bg-opacity: 1 !important;
background-color: #97266d !important;
background-color: rgba(151, 38, 109, var(--bg-opacity)) !important;
}
.bg-pink-900 {
--bg-opacity: 1 !important;
background-color: #702459 !important;
background-color: rgba(112, 36, 89, var(--bg-opacity)) !important;
}
.hover\:bg-transparent:hover {
background-color: transparent !important;
}
.hover\:bg-current:hover {
background-color: currentColor !important;
}
.hover\:bg-black:hover {
--bg-opacity: 1 !important;
background-color: #000 !important;
background-color: rgba(0, 0, 0, var(--bg-opacity)) !important;
}
.hover\:bg-white:hover {
--bg-opacity: 1 !important;
background-color: #fff !important;
background-color: rgba(255, 255, 255, var(--bg-opacity)) !important;
}
.hover\:bg-gray-100:hover {
--bg-opacity: 1 !important;
background-color: #f7fafc !important;
background-color: rgba(247, 250, 252, var(--bg-opacity)) !important;
}
.hover\:bg-gray-200:hover {
--bg-opacity: 1 !important;
background-color: #edf2f7 !important;
background-color: rgba(237, 242, 247, var(--bg-opacity)) !important;
}
.hover\:bg-gray-300:hover {
--bg-opacity: 1 !important;
background-color: #e2e8f0 !important;
background-color: rgba(226, 232, 240, var(--bg-opacity)) !important;
}
.hover\:bg-gray-400:hover {
--bg-opacity: 1 !important;
background-color: #cbd5e0 !important;
background-color: rgba(203, 213, 224, var(--bg-opacity)) !important;
}
.hover\:bg-gray-500:hover {
--bg-opacity: 1 !important;
background-color: #a0aec0 !important;
background-color: rgba(160, 174, 192, var(--bg-opacity)) !important;
}
.hover\:bg-gray-600:hover {
--bg-opacity: 1 !important;
background-color: #718096 !important;
background-color: rgba(113, 128, 150, var(--bg-opacity)) !important;
}
.hover\:bg-gray-700:hover {
--bg-opacity: 1 !important;
background-color: #4a5568 !important;
background-color: rgba(74, 85, 104, var(--bg-opacity)) !important;
}
.hover\:bg-gray-800:hover {
--bg-opacity: 1 !important;
background-color: #2d3748 !important;
background-color: rgba(45, 55, 72, var(--bg-opacity)) !important;
}
.hover\:bg-gray-900:hover {
--bg-opacity: 1 !important;
background-color: #1a202c !important;
background-color: rgba(26, 32, 44, var(--bg-opacity)) !important;
}
.hover\:bg-red-100:hover {
--bg-opacity: 1 !important;
background-color: #fff5f5 !important;
background-color: rgba(255, 245, 245, var(--bg-opacity)) !important;
}
.hover\:bg-red-200:hover {
--bg-opacity: 1 !important;
background-color: #fed7d7 !important;
background-color: rgba(254, 215, 215, var(--bg-opacity)) !important;
}
.hover\:bg-red-300:hover {
--bg-opacity: 1 !important;
background-color: #feb2b2 !important;
background-color: rgba(254, 178, 178, var(--bg-opacity)) !important;
}
.hover\:bg-red-400:hover {
--bg-opacity: 1 !important;
background-color: #fc8181 !important;
background-color: rgba(252, 129, 129, var(--bg-opacity)) !important;
}
.hover\:bg-red-500:hover {
--bg-opacity: 1 !important;
background-color: #f56565 !important;
background-color: rgba(245, 101, 101, var(--bg-opacity)) !important;
}
.hover\:bg-red-600:hover {
--bg-opacity: 1 !important;
background-color: #e53e3e !important;
background-color: rgba(229, 62, 62, var(--bg-opacity)) !important;
}
.hover\:bg-red-700:hover {
--bg-opacity: 1 !important;
background-color: #c53030 !important;
background-color: rgba(197, 48, 48, var(--bg-opacity)) !important;
}
.hover\:bg-red-800:hover {
--bg-opacity: 1 !important;
background-color: #9b2c2c !important;
background-color: rgba(155, 44, 44, var(--bg-opacity)) !important;
}
.hover\:bg-red-900:hover {
--bg-opacity: 1 !important;
background-color: #742a2a !important;
background-color: rgba(116, 42, 42, var(--bg-opacity)) !important;
}
.hover\:bg-orange-100:hover {
--bg-opacity: 1 !important;
background-color: #fffaf0 !important;
background-color: rgba(255, 250, 240, var(--bg-opacity)) !important;
}
.hover\:bg-orange-200:hover {
--bg-opacity: 1 !important;
background-color: #feebc8 !important;
background-color: rgba(254, 235, 200, var(--bg-opacity)) !important;
}
.hover\:bg-orange-300:hover {
--bg-opacity: 1 !important;
background-color: #fbd38d !important;
background-color: rgba(251, 211, 141, var(--bg-opacity)) !important;
}
.hover\:bg-orange-400:hover {
--bg-opacity: 1 !important;
background-color: #f6ad55 !important;
background-color: rgba(246, 173, 85, var(--bg-opacity)) !important;
}
.hover\:bg-orange-500:hover {
--bg-opacity: 1 !important;
background-color: #ed8936 !important;
background-color: rgba(237, 137, 54, var(--bg-opacity)) !important;
}
.hover\:bg-orange-600:hover {
--bg-opacity: 1 !important;
background-color: #dd6b20 !important;
background-color: rgba(221, 107, 32, var(--bg-opacity)) !important;
}
.hover\:bg-orange-700:hover {
--bg-opacity: 1 !important;
background-color: #c05621 !important;
background-color: rgba(192, 86, 33, var(--bg-opacity)) !important;
}
.hover\:bg-orange-800:hover {
--bg-opacity: 1 !important;
background-color: #9c4221 !important;
background-color: rgba(156, 66, 33, var(--bg-opacity)) !important;
}
.hover\:bg-orange-900:hover {
--bg-opacity: 1 !important;
background-color: #7b341e !important;
background-color: rgba(123, 52, 30, var(--bg-opacity)) !important;
}
.hover\:bg-yellow-100:hover {
--bg-opacity: 1 !important;
background-color: #fffff0 !important;
background-color: rgba(255, 255, 240, var(--bg-opacity)) !important;
}
.hover\:bg-yellow-200:hover {
--bg-opacity: 1 !important;
background-color: #fefcbf !important;
background-color: rgba(254, 252, 191, var(--bg-opacity)) !important;
}
.hover\:bg-yellow-300:hover {
--bg-opacity: 1 !important;
background-color: #faf089 !important;
background-color: rgba(250, 240, 137, var(--bg-opacity)) !important;
}
.hover\:bg-yellow-400:hover {
--bg-opacity: 1 !important;
background-color: #f6e05e !important;
background-color: rgba(246, 224, 94, var(--bg-opacity)) !important;
}
.hover\:bg-yellow-500:hover {
--bg-opacity: 1 !important;
background-color: #ecc94b !important;
background-color: rgba(236, 201, 75, var(--bg-opacity)) !important;
}
.hover\:bg-yellow-600:hover {
--bg-opacity: 1 !important;
background-color: #d69e2e !important;
background-color: rgba(214, 158, 46, var(--bg-opacity)) !important;
}
.hover\:bg-yellow-700:hover {
--bg-opacity: 1 !important;
background-color: #b7791f !important;
background-color: rgba(183, 121, 31, var(--bg-opacity)) !important;
}
.hover\:bg-yellow-800:hover {
--bg-opacity: 1 !important;
background-color: #975a16 !important;
background-color: rgba(151, 90, 22, var(--bg-opacity)) !important;
}
.hover\:bg-yellow-900:hover {
--bg-opacity: 1 !important;
background-color: #744210 !important;
background-color: rgba(116, 66, 16, var(--bg-opacity)) !important;
}
.hover\:bg-green-100:hover {
--bg-opacity: 1 !important;
background-color: #f0fff4 !important;
background-color: rgba(240, 255, 244, var(--bg-opacity)) !important;
}
.hover\:bg-green-200:hover {
--bg-opacity: 1 !important;
background-color: #c6f6d5 !important;
background-color: rgba(198, 246, 213, var(--bg-opacity)) !important;
}
.hover\:bg-green-300:hover {
--bg-opacity: 1 !important;
background-color: #9ae6b4 !important;
background-color: rgba(154, 230, 180, var(--bg-opacity)) !important;
}
.hover\:bg-green-400:hover {
--bg-opacity: 1 !important;
background-color: #68d391 !important;
background-color: rgba(104, 211, 145, var(--bg-opacity)) !important;
}
.hover\:bg-green-500:hover {
--bg-opacity: 1 !important;
background-color: #48bb78 !important;
background-color: rgba(72, 187, 120, var(--bg-opacity)) !important;
}
.hover\:bg-green-600:hover {
--bg-opacity: 1 !important;
background-color: #38a169 !important;
background-color: rgba(56, 161, 105, var(--bg-opacity)) !important;
}
.hover\:bg-green-700:hover {
--bg-opacity: 1 !important;
background-color: #2f855a !important;
background-color: rgba(47, 133, 90, var(--bg-opacity)) !important;
}
.hover\:bg-green-800:hover {
--bg-opacity: 1 !important;
background-color: #276749 !important;
background-color: rgba(39, 103, 73, var(--bg-opacity)) !important;
}
.hover\:bg-green-900:hover {
--bg-opacity: 1 !important;
background-color: #22543d !important;
background-color: rgba(34, 84, 61, var(--bg-opacity)) !important;
}
.hover\:bg-teal-100:hover {
--bg-opacity: 1 !important;
background-color: #e6fffa !important;
background-color: rgba(230, 255, 250, var(--bg-opacity)) !important;
}
.hover\:bg-teal-200:hover {
--bg-opacity: 1 !important;
background-color: #b2f5ea !important;
background-color: rgba(178, 245, 234, var(--bg-opacity)) !important;
}
.hover\:bg-teal-300:hover {
--bg-opacity: 1 !important;
background-color: #81e6d9 !important;
background-color: rgba(129, 230, 217, var(--bg-opacity)) !important;
}
.hover\:bg-teal-400:hover {
--bg-opacity: 1 !important;
background-color: #4fd1c5 !important;
background-color: rgba(79, 209, 197, var(--bg-opacity)) !important;
}
.hover\:bg-teal-500:hover {
--bg-opacity: 1 !important;
background-color: #38b2ac !important;
background-color: rgba(56, 178, 172, var(--bg-opacity)) !important;
}
.hover\:bg-teal-600:hover {
--bg-opacity: 1 !important;
background-color: #319795 !important;
background-color: rgba(49, 151, 149, var(--bg-opacity)) !important;
}
.hover\:bg-teal-700:hover {
--bg-opacity: 1 !important;
background-color: #2c7a7b !important;
background-color: rgba(44, 122, 123, var(--bg-opacity)) !important;
}
.hover\:bg-teal-800:hover {
--bg-opacity: 1 !important;
background-color: #285e61 !important;
background-color: rgba(40, 94, 97, var(--bg-opacity)) !important;
}
.hover\:bg-teal-900:hover {
--bg-opacity: 1 !important;
background-color: #234e52 !important;
background-color: rgba(35, 78, 82, var(--bg-opacity)) !important;
}
.hover\:bg-blue-100:hover {
--bg-opacity: 1 !important;
background-color: #ebf8ff !important;
background-color: rgba(235, 248, 255, var(--bg-opacity)) !important;
}
.hover\:bg-blue-200:hover {
--bg-opacity: 1 !important;
background-color: #bee3f8 !important;
background-color: rgba(190, 227, 248, var(--bg-opacity)) !important;
}
.hover\:bg-blue-300:hover {
--bg-opacity: 1 !important;
background-color: #90cdf4 !important;
background-color: rgba(144, 205, 244, var(--bg-opacity)) !important;
}
.hover\:bg-blue-400:hover {
--bg-opacity: 1 !important;
background-color: #63b3ed !important;
background-color: rgba(99, 179, 237, var(--bg-opacity)) !important;
}
.hover\:bg-blue-500:hover {
--bg-opacity: 1 !important;
background-color: #4299e1 !important;
background-color: rgba(66, 153, 225, var(--bg-opacity)) !important;
}
.hover\:bg-blue-600:hover {
--bg-opacity: 1 !important;
background-color: #3182ce !important;
background-color: rgba(49, 130, 206, var(--bg-opacity)) !important;
}
.hover\:bg-blue-700:hover {
--bg-opacity: 1 !important;
background-color: #2b6cb0 !important;
background-color: rgba(43, 108, 176, var(--bg-opacity)) !important;
}
.hover\:bg-blue-800:hover {
--bg-opacity: 1 !important;
background-color: #2c5282 !important;
background-color: rgba(44, 82, 130, var(--bg-opacity)) !important;
}
.hover\:bg-blue-900:hover {
--bg-opacity: 1 !important;
background-color: #2a4365 !important;
background-color: rgba(42, 67, 101, var(--bg-opacity)) !important;
}
.hover\:bg-indigo-100:hover {
--bg-opacity: 1 !important;
background-color: #ebf4ff !important;
background-color: rgba(235, 244, 255, var(--bg-opacity)) !important;
}
.hover\:bg-indigo-200:hover {
--bg-opacity: 1 !important;
background-color: #c3dafe !important;
background-color: rgba(195, 218, 254, var(--bg-opacity)) !important;
}
.hover\:bg-indigo-300:hover {
--bg-opacity: 1 !important;
background-color: #a3bffa !important;
background-color: rgba(163, 191, 250, var(--bg-opacity)) !important;
}
.hover\:bg-indigo-400:hover {
--bg-opacity: 1 !important;
background-color: #7f9cf5 !important;
background-color: rgba(127, 156, 245, var(--bg-opacity)) !important;
}
.hover\:bg-indigo-500:hover {
--bg-opacity: 1 !important;
background-color: #667eea !important;
background-color: rgba(102, 126, 234, var(--bg-opacity)) !important;
}
.hover\:bg-indigo-600:hover {
--bg-opacity: 1 !important;
background-color: #5a67d8 !important;
background-color: rgba(90, 103, 216, var(--bg-opacity)) !important;
}
.hover\:bg-indigo-700:hover {
--bg-opacity: 1 !important;
background-color: #4c51bf !important;
background-color: rgba(76, 81, 191, var(--bg-opacity)) !important;
}
.hover\:bg-indigo-800:hover {
--bg-opacity: 1 !important;
background-color: #434190 !important;
background-color: rgba(67, 65, 144, var(--bg-opacity)) !important;
}
.hover\:bg-indigo-900:hover {
--bg-opacity: 1 !important;
background-color: #3c366b !important;
background-color: rgba(60, 54, 107, var(--bg-opacity)) !important;
}
.hover\:bg-purple-100:hover {
--bg-opacity: 1 !important;
background-color: #faf5ff !important;
background-color: rgba(250, 245, 255, var(--bg-opacity)) !important;
}
.hover\:bg-purple-200:hover {
--bg-opacity: 1 !important;
background-color: #e9d8fd !important;
background-color: rgba(233, 216, 253, var(--bg-opacity)) !important;
}
.hover\:bg-purple-300:hover {
--bg-opacity: 1 !important;
background-color: #d6bcfa !important;
background-color: rgba(214, 188, 250, var(--bg-opacity)) !important;
}
.hover\:bg-purple-400:hover {
--bg-opacity: 1 !important;
background-color: #b794f4 !important;
background-color: rgba(183, 148, 244, var(--bg-opacity)) !important;
}
.hover\:bg-purple-500:hover {
--bg-opacity: 1 !important;
background-color: #9f7aea !important;
background-color: rgba(159, 122, 234, var(--bg-opacity)) !important;
}
.hover\:bg-purple-600:hover {
--bg-opacity: 1 !important;
background-color: #805ad5 !important;
background-color: rgba(128, 90, 213, var(--bg-opacity)) !important;
}
.hover\:bg-purple-700:hover {
--bg-opacity: 1 !important;
background-color: #6b46c1 !important;
background-color: rgba(107, 70, 193, var(--bg-opacity)) !important;
}
.hover\:bg-purple-800:hover {
--bg-opacity: 1 !important;
background-color: #553c9a !important;
background-color: rgba(85, 60, 154, var(--bg-opacity)) !important;
}
.hover\:bg-purple-900:hover {
--bg-opacity: 1 !important;
background-color: #44337a !important;
background-color: rgba(68, 51, 122, var(--bg-opacity)) !important;
}
.hover\:bg-pink-100:hover {
--bg-opacity: 1 !important;
background-color: #fff5f7 !important;
background-color: rgba(255, 245, 247, var(--bg-opacity)) !important;
}
.hover\:bg-pink-200:hover {
--bg-opacity: 1 !important;
background-color: #fed7e2 !important;
background-color: rgba(254, 215, 226, var(--bg-opacity)) !important;
}
.hover\:bg-pink-300:hover {
--bg-opacity: 1 !important;
background-color: #fbb6ce !important;
background-color: rgba(251, 182, 206, var(--bg-opacity)) !important;
}
.hover\:bg-pink-400:hover {
--bg-opacity: 1 !important;
background-color: #f687b3 !important;
background-color: rgba(246, 135, 179, var(--bg-opacity)) !important;
}
.hover\:bg-pink-500:hover {
--bg-opacity: 1 !important;
background-color: #ed64a6 !important;
background-color: rgba(237, 100, 166, var(--bg-opacity)) !important;
}
.hover\:bg-pink-600:hover {
--bg-opacity: 1 !important;
background-color: #d53f8c !important;
background-color: rgba(213, 63, 140, var(--bg-opacity)) !important;
}
.hover\:bg-pink-700:hover {
--bg-opacity: 1 !important;
background-color: #b83280 !important;
background-color: rgba(184, 50, 128, var(--bg-opacity)) !important;
}
.hover\:bg-pink-800:hover {
--bg-opacity: 1 !important;
background-color: #97266d !important;
background-color: rgba(151, 38, 109, var(--bg-opacity)) !important;
}
.hover\:bg-pink-900:hover {
--bg-opacity: 1 !important;
background-color: #702459 !important;
background-color: rgba(112, 36, 89, var(--bg-opacity)) !important;
}
.focus\:bg-transparent:focus {
background-color: transparent !important;
}
.focus\:bg-current:focus {
background-color: currentColor !important;
}
.focus\:bg-black:focus {
--bg-opacity: 1 !important;
background-color: #000 !important;
background-color: rgba(0, 0, 0, var(--bg-opacity)) !important;
}
.focus\:bg-white:focus {
--bg-opacity: 1 !important;
background-color: #fff !important;
background-color: rgba(255, 255, 255, var(--bg-opacity)) !important;
}
.focus\:bg-gray-100:focus {
--bg-opacity: 1 !important;
background-color: #f7fafc !important;
background-color: rgba(247, 250, 252, var(--bg-opacity)) !important;
}
.focus\:bg-gray-200:focus {
--bg-opacity: 1 !important;
background-color: #edf2f7 !important;
background-color: rgba(237, 242, 247, var(--bg-opacity)) !important;
}
.focus\:bg-gray-300:focus {
--bg-opacity: 1 !important;
background-color: #e2e8f0 !important;
background-color: rgba(226, 232, 240, var(--bg-opacity)) !important;
}
.focus\:bg-gray-400:focus {
--bg-opacity: 1 !important;
background-color: #cbd5e0 !important;
background-color: rgba(203, 213, 224, var(--bg-opacity)) !important;
}
.focus\:bg-gray-500:focus {
--bg-opacity: 1 !important;
background-color: #a0aec0 !important;
background-color: rgba(160, 174, 192, var(--bg-opacity)) !important;
}
.focus\:bg-gray-600:focus {
--bg-opacity: 1 !important;
background-color: #718096 !important;
background-color: rgba(113, 128, 150, var(--bg-opacity)) !important;
}
.focus\:bg-gray-700:focus {
--bg-opacity: 1 !important;
background-color: #4a5568 !important;
background-color: rgba(74, 85, 104, var(--bg-opacity)) !important;
}
.focus\:bg-gray-800:focus {
--bg-opacity: 1 !important;
background-color: #2d3748 !important;
background-color: rgba(45, 55, 72, var(--bg-opacity)) !important;
}
.focus\:bg-gray-900:focus {
--bg-opacity: 1 !important;
background-color: #1a202c !important;
background-color: rgba(26, 32, 44, var(--bg-opacity)) !important;
}
.focus\:bg-red-100:focus {
--bg-opacity: 1 !important;
background-color: #fff5f5 !important;
background-color: rgba(255, 245, 245, var(--bg-opacity)) !important;
}
.focus\:bg-red-200:focus {
--bg-opacity: 1 !important;
background-color: #fed7d7 !important;
background-color: rgba(254, 215, 215, var(--bg-opacity)) !important;
}
.focus\:bg-red-300:focus {
--bg-opacity: 1 !important;
background-color: #feb2b2 !important;
background-color: rgba(254, 178, 178, var(--bg-opacity)) !important;
}
.focus\:bg-red-400:focus {
--bg-opacity: 1 !important;
background-color: #fc8181 !important;
background-color: rgba(252, 129, 129, var(--bg-opacity)) !important;
}
.focus\:bg-red-500:focus {
--bg-opacity: 1 !important;
background-color: #f56565 !important;
background-color: rgba(245, 101, 101, var(--bg-opacity)) !important;
}
.focus\:bg-red-600:focus {
--bg-opacity: 1 !important;
background-color: #e53e3e !important;
background-color: rgba(229, 62, 62, var(--bg-opacity)) !important;
}
.focus\:bg-red-700:focus {
--bg-opacity: 1 !important;
background-color: #c53030 !important;
background-color: rgba(197, 48, 48, var(--bg-opacity)) !important;
}
.focus\:bg-red-800:focus {
--bg-opacity: 1 !important;
background-color: #9b2c2c !important;
background-color: rgba(155, 44, 44, var(--bg-opacity)) !important;
}
.focus\:bg-red-900:focus {
--bg-opacity: 1 !important;
background-color: #742a2a !important;
background-color: rgba(116, 42, 42, var(--bg-opacity)) !important;
}
.focus\:bg-orange-100:focus {
--bg-opacity: 1 !important;
background-color: #fffaf0 !important;
background-color: rgba(255, 250, 240, var(--bg-opacity)) !important;
}
.focus\:bg-orange-200:focus {
--bg-opacity: 1 !important;
background-color: #feebc8 !important;
background-color: rgba(254, 235, 200, var(--bg-opacity)) !important;
}
.focus\:bg-orange-300:focus {
--bg-opacity: 1 !important;
background-color: #fbd38d !important;
background-color: rgba(251, 211, 141, var(--bg-opacity)) !important;
}
.focus\:bg-orange-400:focus {
--bg-opacity: 1 !important;
background-color: #f6ad55 !important;
background-color: rgba(246, 173, 85, var(--bg-opacity)) !important;
}
.focus\:bg-orange-500:focus {
--bg-opacity: 1 !important;
background-color: #ed8936 !important;
background-color: rgba(237, 137, 54, var(--bg-opacity)) !important;
}
.focus\:bg-orange-600:focus {
--bg-opacity: 1 !important;
background-color: #dd6b20 !important;
background-color: rgba(221, 107, 32, var(--bg-opacity)) !important;
}
.focus\:bg-orange-700:focus {
--bg-opacity: 1 !important;
background-color: #c05621 !important;
background-color: rgba(192, 86, 33, var(--bg-opacity)) !important;
}
.focus\:bg-orange-800:focus {
--bg-opacity: 1 !important;
background-color: #9c4221 !important;
background-color: rgba(156, 66, 33, var(--bg-opacity)) !important;
}
.focus\:bg-orange-900:focus {
--bg-opacity: 1 !important;
background-color: #7b341e !important;
background-color: rgba(123, 52, 30, var(--bg-opacity)) !important;
}
.focus\:bg-yellow-100:focus {
--bg-opacity: 1 !important;
background-color: #fffff0 !important;
background-color: rgba(255, 255, 240, var(--bg-opacity)) !important;
}
.focus\:bg-yellow-200:focus {
--bg-opacity: 1 !important;
background-color: #fefcbf !important;
background-color: rgba(254, 252, 191, var(--bg-opacity)) !important;
}
.focus\:bg-yellow-300:focus {
--bg-opacity: 1 !important;
background-color: #faf089 !important;
background-color: rgba(250, 240, 137, var(--bg-opacity)) !important;
}
.focus\:bg-yellow-400:focus {
--bg-opacity: 1 !important;
background-color: #f6e05e !important;
background-color: rgba(246, 224, 94, var(--bg-opacity)) !important;
}
.focus\:bg-yellow-500:focus {
--bg-opacity: 1 !important;
background-color: #ecc94b !important;
background-color: rgba(236, 201, 75, var(--bg-opacity)) !important;
}
.focus\:bg-yellow-600:focus {
--bg-opacity: 1 !important;
background-color: #d69e2e !important;
background-color: rgba(214, 158, 46, var(--bg-opacity)) !important;
}
.focus\:bg-yellow-700:focus {
--bg-opacity: 1 !important;
background-color: #b7791f !important;
background-color: rgba(183, 121, 31, var(--bg-opacity)) !important;
}
.focus\:bg-yellow-800:focus {
--bg-opacity: 1 !important;
background-color: #975a16 !important;
background-color: rgba(151, 90, 22, var(--bg-opacity)) !important;
}
.focus\:bg-yellow-900:focus {
--bg-opacity: 1 !important;
background-color: #744210 !important;
background-color: rgba(116, 66, 16, var(--bg-opacity)) !important;
}
.focus\:bg-green-100:focus {
--bg-opacity: 1 !important;
background-color: #f0fff4 !important;
background-color: rgba(240, 255, 244, var(--bg-opacity)) !important;
}
.focus\:bg-green-200:focus {
--bg-opacity: 1 !important;
background-color: #c6f6d5 !important;
background-color: rgba(198, 246, 213, var(--bg-opacity)) !important;
}
.focus\:bg-green-300:focus {
--bg-opacity: 1 !important;
background-color: #9ae6b4 !important;
background-color: rgba(154, 230, 180, var(--bg-opacity)) !important;
}
.focus\:bg-green-400:focus {
--bg-opacity: 1 !important;
background-color: #68d391 !important;
background-color: rgba(104, 211, 145, var(--bg-opacity)) !important;
}
.focus\:bg-green-500:focus {
--bg-opacity: 1 !important;
background-color: #48bb78 !important;
background-color: rgba(72, 187, 120, var(--bg-opacity)) !important;
}
.focus\:bg-green-600:focus {
--bg-opacity: 1 !important;
background-color: #38a169 !important;
background-color: rgba(56, 161, 105, var(--bg-opacity)) !important;
}
.focus\:bg-green-700:focus {
--bg-opacity: 1 !important;
background-color: #2f855a !important;
background-color: rgba(47, 133, 90, var(--bg-opacity)) !important;
}
.focus\:bg-green-800:focus {
--bg-opacity: 1 !important;
background-color: #276749 !important;
background-color: rgba(39, 103, 73, var(--bg-opacity)) !important;
}
.focus\:bg-green-900:focus {
--bg-opacity: 1 !important;
background-color: #22543d !important;
background-color: rgba(34, 84, 61, var(--bg-opacity)) !important;
}
.focus\:bg-teal-100:focus {
--bg-opacity: 1 !important;
background-color: #e6fffa !important;
background-color: rgba(230, 255, 250, var(--bg-opacity)) !important;
}
.focus\:bg-teal-200:focus {
--bg-opacity: 1 !important;
background-color: #b2f5ea !important;
background-color: rgba(178, 245, 234, var(--bg-opacity)) !important;
}
.focus\:bg-teal-300:focus {
--bg-opacity: 1 !important;
background-color: #81e6d9 !important;
background-color: rgba(129, 230, 217, v
gitextract_ls90ifer/
├── .all-contributorsrc
├── .gitattributes
├── .github/
│ └── workflows/
│ ├── bundle-release.js
│ ├── lint_pr.yml
│ ├── pipeline.yml
│ ├── print_esy_cache.js
│ └── release-postinstall.js
├── .gitignore
├── .ocamlformat
├── .spin
├── LICENSE
├── README.md
├── bin/
│ ├── Bin.re
│ ├── UsePpx.re
│ └── dune
├── dune
├── dune-project
├── esy.json
├── js/
│ └── index.js
├── script/
│ ├── release-postinstall.js
│ └── release.sh
├── src/
│ ├── ppx/
│ │ ├── Tailwind_ppx.re
│ │ ├── dune
│ │ ├── levenshtein.re
│ │ ├── option.re
│ │ ├── ppx_config.re
│ │ ├── read_tailwind.re
│ │ └── utils.re
│ └── use_ppx/
│ ├── Paths.re
│ ├── dune
│ └── ext/
│ ├── ext_json_parse.ml
│ ├── ext_json_types.ml
│ └── ext_position.ml
├── tailwind-ppx.opam
├── tailwind-ppx.opam.template
└── test/
├── bucklescript/
│ ├── .gitignore
│ ├── bsconfig.json
│ ├── dune
│ ├── index.css
│ ├── package.json
│ ├── src/
│ │ ├── Demo.re
│ │ ├── index_res_test.res
│ │ └── index_test.re
│ ├── tailwind.config.js
│ └── tailwind_fake.css
├── native/
│ ├── TestRunner.re
│ ├── dune
│ ├── dune-project
│ ├── lib/
│ │ ├── Setup.re
│ │ ├── Test.re
│ │ ├── dune
│ │ └── helpers.ml
│ └── tailwind-ppx-test-native.opam
└── tailwind.css
SYMBOL INDEX (7 symbols across 4 files)
FILE: .github/workflows/print_esy_cache.js
constant ESY_FOLDER (line 5) | const ESY_FOLDER = process.env.ESY__PREFIX
FILE: .github/workflows/release-postinstall.js
function copyRecursive (line 26) | function copyRecursive(srcDir, dstDir) {
function arch (line 62) | function arch() {
function copyFileSync (line 117) | function copyFileSync(sourcePath, destPath) {
FILE: js/index.js
function extractor (line 1) | function extractor(content) {
FILE: script/release-postinstall.js
function find_arch (line 11) | function find_arch() {
function copyFileSync (line 53) | function copyFileSync(sourcePath, destPath) {
Condensed preview — 54 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,286K chars).
[
{
"path": ".all-contributorsrc",
"chars": 3549,
"preview": "{\n \"files\": [\n \"README.md\"\n ],\n \"imageSize\": 100,\n \"commit\": false,\n \"contributors\": [\n {\n \"login\": \"dyl"
},
{
"path": ".gitattributes",
"chars": 598,
"preview": "# Supresses the lock folder from the diffs\nesy.lock/* linguist-generated=true\n\n# Tell github that .re and .rei files are"
},
{
"path": ".github/workflows/bundle-release.js",
"chars": 3241,
"preview": "const fs = require(\"fs\");\nconst path = require(\"path\");\n\nconsole.log(\"Creating package.json\");\n\n// From the project root"
},
{
"path": ".github/workflows/lint_pr.yml",
"chars": 2010,
"preview": "name: Lint and format Pull Request\n\non: [pull_request]\n\njobs:\n lint:\n name: lint lockdir\n runs-on: ubuntu-latest\n"
},
{
"path": ".github/workflows/pipeline.yml",
"chars": 6755,
"preview": "name: tailwind-ppx pipeline\n\non:\n push:\n branches:\n - master\n tags:\n - v*\n pull_request:\n branches:"
},
{
"path": ".github/workflows/print_esy_cache.js",
"chars": 389,
"preview": "const fs = require(\"fs\");\nconst os = require(\"os\");\nconst path = require(\"path\");\n\nconst ESY_FOLDER = process.env.ESY__P"
},
{
"path": ".github/workflows/release-postinstall.js",
"chars": 4874,
"preview": "/**\n * release-postinstall.js\n *\n * XXX: We want to keep this script installable at least with node 4.x.\n *\n * This scri"
},
{
"path": ".gitignore",
"chars": 303,
"preview": "# ocamlbuild working directory\n_build/\n\n# ocamlbuild targets\n*.byte\n*.native\n\n# Merlin configuring file for Vim and Emac"
},
{
"path": ".ocamlformat",
"chars": 0,
"preview": ""
},
{
"path": ".spin",
"chars": 385,
"preview": "(Source ppx)\n(Cfg_str project_name tailwind-ppx)\n(Cfg_str project_slug tailwind-ppx)\n(Cfg_str project_description\"A shor"
},
{
"path": "LICENSE",
"chars": 1069,
"preview": "MIT License\n\nCopyright (c) 2020 Dylan Irlbeck\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
},
{
"path": "README.md",
"chars": 15673,
"preview": "**Note: As of 08/16/2021, `tailwind-ppx` is now in archive-mode. I have been out of the Reason/ReScript community for so"
},
{
"path": "bin/Bin.re",
"chars": 531,
"preview": "open Migrate_parsetree;\n\n// Source: https://github.com/reasonml-community/graphql_ppx/blob/master/src/bucklescript_bin/B"
},
{
"path": "bin/UsePpx.re",
"chars": 1477,
"preview": "/** Reads the contents of a file */\nlet readFile = path => {\n let ch = open_in(path);\n let s = really_input_string(ch,"
},
{
"path": "bin/dune",
"chars": 312,
"preview": "(executable\n (name Bin)\n (modules Bin)\n (public_name tailwind-ppx)\n (libraries tailwind-ppx.lib)\n (ocamlopt_flags (-link"
},
{
"path": "dune",
"chars": 39,
"preview": "(dirs :standard \\ node_modules _build)\n"
},
{
"path": "dune-project",
"chars": 15,
"preview": "(lang dune 2.5)"
},
{
"path": "esy.json",
"chars": 1375,
"preview": "{\n \"name\": \"tailwind-ppx\",\n \"version\": \"0.8.4\",\n \"description\": \"Reason PPX that validates your Tailwind classes at c"
},
{
"path": "js/index.js",
"chars": 413,
"preview": "function extractor(content) {\n const matchedContent = content.match(/(?<=\\%tw\\(?\\s*\")[^\\\"]*(?=\\\")/g);\n\n if (matchedCon"
},
{
"path": "script/release-postinstall.js",
"chars": 2873,
"preview": "#!/usr/bin/env node\n\nconst path = require(\"path\");\nconst cp = require(\"child_process\");\nconst fs = require(\"fs\");\n\nconst"
},
{
"path": "script/release.sh",
"chars": 622,
"preview": "#!/bin/bash\n\nset -e\n\nif [ -d \".git\" ]; then\n changes=$(git status --porcelain)\n branch=$(git rev-parse --abbrev-ref HE"
},
{
"path": "src/ppx/Tailwind_ppx.re",
"chars": 1616,
"preview": "open Migrate_parsetree;\nopen Ast_409;\nopen Ast_mapper;\nopen Parsetree;\nopen Utils;\n\nlet expr = (mapper, e) =>\n switch ("
},
{
"path": "src/ppx/dune",
"chars": 158,
"preview": "(library\n (name tailwind_ppx)\n (wrapped false)\n (public_name tailwind-ppx.lib)\n (kind ppx_rewriter)\n (libraries str ocam"
},
{
"path": "src/ppx/levenshtein.re",
"chars": 867,
"preview": "/** Levenshtein distance\n * Translated from OCaml example in Rosetta Code\n * https://rosettacode.org/wiki/Levenshte"
},
{
"path": "src/ppx/option.re",
"chars": 320,
"preview": "exception Option_unwrap_error;\n\nlet map = f =>\n fun\n | None => None\n | Some(v) => Some(f(v));\n\nlet flat_map = f =>\n "
},
{
"path": "src/ppx/ppx_config.re",
"chars": 383,
"preview": "type config = {\n tailwindFile: string,\n rootDirectory: string,\n};\n\nlet configRef = ref(None);\n\nlet setConfig = config "
},
{
"path": "src/ppx/read_tailwind.re",
"chars": 1098,
"preview": "/** Recursively look for file starting at the project root */\nlet rec findFileTowardsRoot = (dir, file) => {\n let hereF"
},
{
"path": "src/ppx/utils.re",
"chars": 6582,
"preview": "open Css.Types;\nmodule StringSet = Set.Make(String);\n\n/**\n * Splits a string on any whitespace into the individual clas"
},
{
"path": "src/use_ppx/Paths.re",
"chars": 2816,
"preview": "module StringMap = Map.Make(String);\n\nlet projectRoot = ref(\"\");\nlet bsbProjectRoot = ref(\"\");\n\nlet bsconfig = \"bsconfig"
},
{
"path": "src/use_ppx/dune",
"chars": 94,
"preview": "(copy_files# ext/*.{ml,mli})\n\n(library\n (name use_ppx_lib)\n (wrapped false)\n (libraries str))\n"
},
{
"path": "src/use_ppx/ext/ext_json_parse.ml",
"chars": 19668,
"preview": "module StringMap = Map.Make (String)\n\ntype error =\n | Illegal_character of char\n | Unterminated_string\n | Unterminate"
},
{
"path": "src/use_ppx/ext/ext_json_types.ml",
"chars": 1618,
"preview": "(* Copyright (C) 2015-2017 Bloomberg Finance L.P.\n * \n * This program is free software: you can redistribute it and/or m"
},
{
"path": "src/use_ppx/ext/ext_position.ml",
"chars": 1874,
"preview": "(* Copyright (C) 2015-2016 Bloomberg Finance L.P.\n *\n * This program is free software: you can redistribute it and/or mo"
},
{
"path": "tailwind-ppx.opam",
"chars": 806,
"preview": "# This file is generated by dune, edit dune-project instead\nopam-version: \"2.0\"\nsynopsis: \"A short, but powerful stateme"
},
{
"path": "tailwind-ppx.opam.template",
"chars": 198,
"preview": "# We need to avoid \"@runtest\", since it depends on rely\nbuild: [\n [\"dune\" \"subst\"] {pinned}\n [\n \"dune\"\n \"build\"\n"
},
{
"path": "test/bucklescript/.gitignore",
"chars": 112,
"preview": ".merlin\n.bsb.lock\n*.log\n/lib/bs/\nnode_modules\n/build/\n**/*.bs.js\ndist/\nesy.lock\n.cache\n_esy\n.tailwind_ppx_cache\n"
},
{
"path": "test/bucklescript/bsconfig.json",
"chars": 485,
"preview": "{\n \"name\": \"bucklescript\",\n \"reason\": {\n \"react-jsx\": 3\n },\n \"sources\": {\n \"dir\": \"src\",\n \"subdirs\": true,\n"
},
{
"path": "test/bucklescript/dune",
"chars": 32,
"preview": "(dirs :standard \\ node_modules)\n"
},
{
"path": "test/bucklescript/index.css",
"chars": 61,
"preview": "@tailwind base;\n\n@tailwind components;\n\n@tailwind utilities;\n"
},
{
"path": "test/bucklescript/package.json",
"chars": 697,
"preview": "{\n \"name\": \"bucklescript\",\n \"version\": \"0.0.0\",\n \"scripts\": {\n \"build:styles\": \"tailwind build index.css -o ../tai"
},
{
"path": "test/bucklescript/src/Demo.re",
"chars": 200,
"preview": "[@react.component]\nlet make = () => {\n <div\n className=[%tw\n \"flex flex-col items-center justify-center w-full "
},
{
"path": "test/bucklescript/src/index_res_test.res",
"chars": 2005,
"preview": "open Jest\nopen Expect\n\n@bs.module(\"../../../js/index\")\nexternal extractor: string => array<string> = \"extractor\"\n\ndescri"
},
{
"path": "test/bucklescript/src/index_test.re",
"chars": 1860,
"preview": "open Jest;\nopen Expect;\n\n[@bs.module \"../../../js/index\"]\nexternal extractor: string => array(string) = \"extractor\";\n\nde"
},
{
"path": "test/bucklescript/tailwind.config.js",
"chars": 494,
"preview": "module.exports = {\n important: true,\n theme: {},\n extend: {},\n variants: {\n display: [\"responsive\", \"hover\", \"foc"
},
{
"path": "test/bucklescript/tailwind_fake.css",
"chars": 67,
"preview": ".flex {\n display: flex;\n}\n\n.flex-row {\n flex-direction: row;\n}\n\n\n"
},
{
"path": "test/native/TestRunner.re",
"chars": 38,
"preview": "TailwindPpxTestNativeLib.Setup.cli();\n"
},
{
"path": "test/native/dune",
"chars": 119,
"preview": "\n(executable\n (name TestRunner)\n (public_name TailwindPpxTestRunner)\n (libraries\n TailwindPpxTestNativeLib\n )\n)\n"
},
{
"path": "test/native/dune-project",
"chars": 16,
"preview": "(lang dune 1.6)\n"
},
{
"path": "test/native/lib/Setup.re",
"chars": 172,
"preview": "include Rely.Make({\n let config =\n Rely.TestFrameworkConfig.initialize({\n snapshotDir: \"test/native/lib/__snaps"
},
{
"path": "test/native/lib/Test.re",
"chars": 8654,
"preview": "open Setup;\nopen Utils;\nopen Css.Types;\nopen Helpers;\n\ndescribe(\"Main methods\", ({test, _}) => {\n test(\"Parser works fo"
},
{
"path": "test/native/lib/dune",
"chars": 122,
"preview": "\n(library\n (name TailwindPpxTestNativeLib)\n (library_flags (-linkall -g))\n (libraries fmt rely.lib tailwind-ppx.lib)\n"
},
{
"path": "test/native/lib/helpers.ml",
"chars": 2770,
"preview": "open Css.Types\n\nlet rec zip xs ys =\n match (xs, ys) with\n | [], _ -> []\n | _, [] -> []\n | x :: xs, y :: ys -> (x, y)"
},
{
"path": "test/native/tailwind-ppx-test-native.opam",
"chars": 0,
"preview": ""
},
{
"path": "test/tailwind.css",
"chars": 2076913,
"preview": "/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ==========================="
}
]
About this extraction
This page contains the full source code of the dylanirlbeck/tailwind-ppx GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 54 files (2.1 MB), approximately 547.2k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.