mirror of
https://github.com/tormachris/cf-workers-status-page.git
synced 2024-11-23 14:45:41 +01:00
refactor: switch css framework to tailwind
This commit is contained in:
parent
42f422c455
commit
fb134bbf74
Binary file not shown.
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 450 KiB |
1
.gitignore
vendored
1
.gitignore
vendored
@ -133,3 +133,4 @@ worker/
|
|||||||
.direnv/
|
.direnv/
|
||||||
out/
|
out/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
public/style.css
|
||||||
|
4
index.js
4
index.js
@ -10,7 +10,7 @@ import { processCronTrigger } from './src/functions/cronTrigger'
|
|||||||
*/
|
*/
|
||||||
const DEBUG = false
|
const DEBUG = false
|
||||||
|
|
||||||
addEventListener('fetch', event => {
|
addEventListener('fetch', (event) => {
|
||||||
try {
|
try {
|
||||||
event.respondWith(
|
event.respondWith(
|
||||||
handleEvent(event, require.context('./pages/', true, /\.js$/), DEBUG),
|
handleEvent(event, require.context('./pages/', true, /\.js$/), DEBUG),
|
||||||
@ -27,6 +27,6 @@ addEventListener('fetch', event => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
addEventListener('scheduled', event => {
|
addEventListener('scheduled', (event) => {
|
||||||
event.waitUntil(processCronTrigger(event))
|
event.waitUntil(processCronTrigger(event))
|
||||||
})
|
})
|
||||||
|
17
package.json
17
package.json
@ -7,21 +7,26 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "flareact dev",
|
"dev": "flareact dev",
|
||||||
"build": "flareact build",
|
"build": "yarn css && flareact build",
|
||||||
"deploy": "flareact publish",
|
"deploy": "yarn build && flareact publish",
|
||||||
"kv-gc": "node ./src/cli/gcMonitors.js",
|
"kv-gc": "node ./src/cli/gcMonitors.js",
|
||||||
"format": "prettier --write '**/*.{js,css,json,md}'"
|
"format": "prettier --write '**/*.{js,css,json,md}'",
|
||||||
|
"css": "postcss public/tailwind.css -o public/style.css"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"flareact": "^0.9.0",
|
"flareact": "^0.9.0",
|
||||||
"laco": "^1.2.1",
|
"laco": "^1.2.1",
|
||||||
"laco-react": "^1.1.0",
|
"laco-react": "^1.1.0",
|
||||||
"react": "^16.13.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^16.13.1"
|
"react-dom": "^17.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^10.0.2",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"prettier": "^1.18.2",
|
"postcss": "^8.1.8",
|
||||||
|
"postcss-cli": "^8.3.0",
|
||||||
|
"prettier": "^2.2.0",
|
||||||
|
"tailwindcss": "^2.0.1",
|
||||||
"yaml-loader": "^0.6.0"
|
"yaml-loader": "^0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { processCronTrigger } from '../../src/functions/cronTrigger'
|
import { processCronTrigger } from '../../src/functions/cronTrigger'
|
||||||
|
|
||||||
export default async event => {
|
export default async (event) => {
|
||||||
// used only for local debugging
|
// used only for local debugging
|
||||||
//return processCronTrigger(event)
|
//return processCronTrigger(event)
|
||||||
}
|
}
|
||||||
|
155
pages/index.js
155
pages/index.js
@ -1,115 +1,97 @@
|
|||||||
import Head from 'flareact/head'
|
|
||||||
import MonitorHistogram from '../src/components/monitorHistogram'
|
|
||||||
|
|
||||||
import {
|
|
||||||
getMonitors,
|
|
||||||
useKeyPress,
|
|
||||||
} from '../src/functions/helpers'
|
|
||||||
|
|
||||||
import config from '../config.yaml'
|
|
||||||
import MonitorStatusLabel from '../src/components/monitorStatusLabel'
|
|
||||||
import MonitorStatusHeader from '../src/components/monitorStatusHeader'
|
|
||||||
import MonitorFilter from '../src/components/monitorFilter'
|
|
||||||
|
|
||||||
import { Store } from 'laco'
|
import { Store } from 'laco'
|
||||||
import { useStore } from 'laco-react'
|
import { useStore } from 'laco-react'
|
||||||
|
import Head from 'flareact/head'
|
||||||
|
|
||||||
const MonitorStore = new Store(
|
import { getMonitors, useKeyPress, switchTheme } from '../src/functions/helpers'
|
||||||
{
|
import config from '../config.yaml'
|
||||||
monitors: config.monitors,
|
import MonitorCard from '../src/components/monitorCard'
|
||||||
visible: config.monitors,
|
import MonitorFilter from '../src/components/monitorFilter'
|
||||||
activeFilter: false
|
import MonitorStatusHeader from '../src/components/monitorStatusHeader'
|
||||||
}
|
import ThemeSwitcher from '../src/components/themeSwitcher'
|
||||||
)
|
|
||||||
|
|
||||||
const filterByTerm = (term) => MonitorStore.set(
|
const MonitorStore = new Store({
|
||||||
state => ({ visible: state.monitors.filter((monitor) => monitor.name.toLowerCase().includes(term)) })
|
monitors: config.monitors,
|
||||||
)
|
visible: config.monitors,
|
||||||
|
activeFilter: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const filterByTerm = (term) =>
|
||||||
|
MonitorStore.set((state) => ({
|
||||||
|
visible: state.monitors.filter((monitor) =>
|
||||||
|
monitor.name.toLowerCase().includes(term),
|
||||||
|
),
|
||||||
|
}))
|
||||||
|
|
||||||
export async function getEdgeProps() {
|
export async function getEdgeProps() {
|
||||||
// get KV data
|
// get KV data
|
||||||
const {value: kvMonitors, metadata: kvMonitorsMetadata } = await getMonitors()
|
const {
|
||||||
|
value: kvMonitors,
|
||||||
|
metadata: kvMonitorsMetadata,
|
||||||
|
} = await getMonitors()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
config,
|
config,
|
||||||
kvMonitors: kvMonitors || {},
|
kvMonitors: kvMonitors || {},
|
||||||
kvMonitorsMetadata: kvMonitorsMetadata || {}
|
kvMonitorsMetadata: kvMonitorsMetadata || {},
|
||||||
},
|
},
|
||||||
// Revalidate these props once every x seconds
|
// Revalidate these props once every x seconds
|
||||||
revalidate: 5,
|
revalidate: 5,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Index({
|
export default function Index({ config, kvMonitors, kvMonitorsMetadata }) {
|
||||||
config,
|
|
||||||
kvMonitors,
|
|
||||||
kvMonitorsMetadata,
|
|
||||||
}) {
|
|
||||||
const state = useStore(MonitorStore)
|
const state = useStore(MonitorStore)
|
||||||
const slash = useKeyPress('/')
|
const slash = useKeyPress('/')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="min-h-screen">
|
||||||
<Head>
|
<Head>
|
||||||
<title>{config.settings.title}</title>
|
<title>{config.settings.title}</title>
|
||||||
<link
|
<link rel="stylesheet" href="./style.css" />
|
||||||
rel="stylesheet"
|
<script>
|
||||||
href="https://cdnjs.cloudflare.com/ajax/libs/fomantic-ui/2.8.7/semantic.min.css"
|
{`
|
||||||
crossOrigin="anonymous"
|
function setTheme(theme) {
|
||||||
/>
|
document.documentElement.classList.remove("dark", "light")
|
||||||
<link rel="stylesheet" href="./main.css" />
|
document.documentElement.classList.add(theme)
|
||||||
|
localStorage.theme = theme
|
||||||
|
}
|
||||||
|
(() => {
|
||||||
|
const query = window.matchMedia("(prefers-color-scheme: dark)")
|
||||||
|
query.addListener(() => {
|
||||||
|
setTheme(query.matches ? "dark" : "light")
|
||||||
|
})
|
||||||
|
if (["dark", "light"].includes(localStorage.theme)) {
|
||||||
|
setTheme(localStorage.theme)
|
||||||
|
} else {
|
||||||
|
setTheme(query.matches ? "dark" : "light")
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
`}
|
||||||
|
</script>
|
||||||
</Head>
|
</Head>
|
||||||
<div className="ui basic segment container">
|
<div className="container mx-auto px-4">
|
||||||
<div className="horizontal flex between">
|
<div className="flex flex-row justify-between items-center p-4">
|
||||||
<h1 className="ui huge marginless title header">
|
<div className="flex flex-row items-center">
|
||||||
<img
|
<img className="h-8 w-auto" src={config.settings.logo} />
|
||||||
className="ui middle aligned tiny image"
|
<h1 className="ml-4 text-3xl">{config.settings.title}</h1>
|
||||||
src={config.settings.logo}
|
</div>
|
||||||
/>
|
<div className="flex flex-row items-center">
|
||||||
{config.settings.title}
|
{typeof window !== 'undefined' && <ThemeSwitcher />}
|
||||||
</h1>
|
<MonitorFilter active={slash} callback={filterByTerm} />
|
||||||
<MonitorFilter
|
</div>
|
||||||
active={slash}
|
|
||||||
callback={filterByTerm}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<MonitorStatusHeader
|
<MonitorStatusHeader kvMonitorsMetadata={kvMonitorsMetadata} />
|
||||||
kvMonitorsMetadata={kvMonitorsMetadata}
|
|
||||||
/>
|
|
||||||
{state.visible.map((monitor, key) => {
|
{state.visible.map((monitor, key) => {
|
||||||
return (
|
return (
|
||||||
<div key={key} className="ui segment">
|
<MonitorCard
|
||||||
<div
|
key={key}
|
||||||
className="ui horizontal flex between"
|
monitor={monitor}
|
||||||
style={{ marginBottom: '8px' }}
|
data={kvMonitors[monitor.id]}
|
||||||
>
|
/>
|
||||||
<div className="ui marginless header">
|
|
||||||
{monitor.description && (
|
|
||||||
<span data-tooltip={monitor.description}>
|
|
||||||
<i className="blue small info circle icon" />
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<div className="content">{monitor.name}</div>
|
|
||||||
</div>
|
|
||||||
<MonitorStatusLabel
|
|
||||||
kvMonitor={kvMonitors[monitor.id]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<MonitorHistogram
|
|
||||||
monitorId={monitor.id}
|
|
||||||
kvMonitor={kvMonitors[monitor.id]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="horizontal flex between grey-text">
|
|
||||||
<div>{config.settings.daysInHistogram} days ago</div>
|
|
||||||
<div>Today</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
<div className="horizontal flex between grey-text">
|
<div className="flex flex-row justify-between mt-4 text-sm">
|
||||||
<div>
|
<div>
|
||||||
Powered by{' '}
|
Powered by{' '}
|
||||||
<a href="https://workers.cloudflare.com/" target="_blank">
|
<a href="https://workers.cloudflare.com/" target="_blank">
|
||||||
@ -129,6 +111,15 @@ export default function Index({
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
{`
|
||||||
|
function setTheme(theme) {
|
||||||
|
document.documentElement.classList.remove("dark", "light")
|
||||||
|
document.documentElement.classList.add(theme)
|
||||||
|
localStorage.theme = theme
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
@ -1,68 +0,0 @@
|
|||||||
body {
|
|
||||||
background: #eeeeee;
|
|
||||||
}
|
|
||||||
.flex {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.flex.horizontal {
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
.flex.vertical {
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.flex.between {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.marginless {
|
|
||||||
margin: 0 !important;
|
|
||||||
}
|
|
||||||
.paddingless {
|
|
||||||
padding: 0 !important;
|
|
||||||
}
|
|
||||||
.black-text {
|
|
||||||
color: #000 !important;
|
|
||||||
}
|
|
||||||
.grey-text {
|
|
||||||
color: #a0a0a0 !important;
|
|
||||||
}
|
|
||||||
.white-text {
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
.histogram {
|
|
||||||
height: 24px;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.hitbox {
|
|
||||||
align-items: flex-end;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
padding: 1px;
|
|
||||||
border-radius: 3.75px;
|
|
||||||
}
|
|
||||||
.bar {
|
|
||||||
background: #dcddde;
|
|
||||||
padding-bottom: 1px;
|
|
||||||
height: 100%;
|
|
||||||
width: 85%;
|
|
||||||
border-radius: 100px;
|
|
||||||
}
|
|
||||||
.bar.green {
|
|
||||||
background: #21ba45;
|
|
||||||
}
|
|
||||||
.bar.red {
|
|
||||||
background: #db2828;
|
|
||||||
}
|
|
||||||
.bar.orange {
|
|
||||||
background: #f2711c;
|
|
||||||
}
|
|
||||||
span i.icon {
|
|
||||||
margin: 0 .25em .25em 0 !important;
|
|
||||||
}
|
|
||||||
.ui.title.header .ui.image {
|
|
||||||
margin-top: -.5em !important;
|
|
||||||
}
|
|
68
public/tailwind.css
Normal file
68
public/tailwind.css
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
body {
|
||||||
|
@apply bg-gray-100 text-gray-700 dark:bg-gray-800 dark:text-gray-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
@apply text-blue-500 dark:text-blue-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer components {
|
||||||
|
.card {
|
||||||
|
@apply p-4 bg-white border border-gray-200 dark:bg-gray-700 dark:border-gray-600 shadow rounded-lg p-4 mb-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill {
|
||||||
|
@apply px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
.histogram {
|
||||||
|
@apply h-6 w-full mx-auto;
|
||||||
|
}
|
||||||
|
.hitbox {
|
||||||
|
align-items: flex-end;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
padding: 1px;
|
||||||
|
border-radius: 3.75px;
|
||||||
|
}
|
||||||
|
.bar {
|
||||||
|
@apply bg-gray-300 dark:bg-gray-600;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
height: 100%;
|
||||||
|
width: 85%;
|
||||||
|
border-radius: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar.green {
|
||||||
|
@apply bg-green-400 dark:bg-green-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar.red {
|
||||||
|
@apply bg-red-400 dark:bg-red-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar.yellow {
|
||||||
|
@apply bg-yellow-400 dark:bg-yellow-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
@apply relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip .content {
|
||||||
|
@apply invisible absolute z-50 inline-block;
|
||||||
|
@apply rounded-full py-1 px-2 bg-gray-100 dark:bg-gray-800 shadow;
|
||||||
|
@apply opacity-0 transition-all duration-200 scale-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover .content {
|
||||||
|
@apply visible opacity-100 scale-100;
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,9 @@ const apiToken = process.env.CF_API_TOKEN
|
|||||||
const kvPrefix = 's_'
|
const kvPrefix = 's_'
|
||||||
|
|
||||||
if (!accountId || !namespaceId || !apiToken) {
|
if (!accountId || !namespaceId || !apiToken) {
|
||||||
console.error("Missing required environment variables: CF_ACCOUNT_ID, KV_NAMESPACE_ID, CF_API_TOKEN")
|
console.error(
|
||||||
|
'Missing required environment variables: CF_ACCOUNT_ID, KV_NAMESPACE_ID, CF_API_TOKEN',
|
||||||
|
)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,24 +53,26 @@ function loadConfig() {
|
|||||||
return JSON.parse(config)
|
return JSON.parse(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
getKvMonitors(kvPrefix).then(async kvMonitors => {
|
getKvMonitors(kvPrefix)
|
||||||
const config = loadConfig()
|
.then(async (kvMonitors) => {
|
||||||
const monitors = config.monitors.map(key => {
|
const config = loadConfig()
|
||||||
return key.id
|
const monitors = config.monitors.map((key) => {
|
||||||
})
|
return key.id
|
||||||
const kvState = kvMonitors.map(key => {
|
})
|
||||||
return key.name
|
const kvState = kvMonitors.map((key) => {
|
||||||
})
|
return key.name
|
||||||
const keysForRemoval = kvState.filter(
|
})
|
||||||
x => !monitors.includes(x.replace(kvPrefix, '')),
|
const keysForRemoval = kvState.filter(
|
||||||
)
|
(x) => !monitors.includes(x.replace(kvPrefix, '')),
|
||||||
|
|
||||||
if (keysForRemoval.length > 0) {
|
|
||||||
console.log(
|
|
||||||
`Removing following keys from KV storage as they are no longer in the config: ${keysForRemoval.join(
|
|
||||||
', ',
|
|
||||||
)}`,
|
|
||||||
)
|
)
|
||||||
await deleteKvBulk(keysForRemoval)
|
|
||||||
}
|
if (keysForRemoval.length > 0) {
|
||||||
}).catch(e => console.log(e))
|
console.log(
|
||||||
|
`Removing following keys from KV storage as they are no longer in the config: ${keysForRemoval.join(
|
||||||
|
', ',
|
||||||
|
)}`,
|
||||||
|
)
|
||||||
|
await deleteKvBulk(keysForRemoval)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e) => console.log(e))
|
||||||
|
46
src/components/monitorCard.js
Normal file
46
src/components/monitorCard.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import config from '../../config.yaml'
|
||||||
|
import MonitorStatusLabel from './monitorStatusLabel'
|
||||||
|
import MonitorHistogram from './monitorHistogram'
|
||||||
|
|
||||||
|
const infoIcon = (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
fill="currentColor"
|
||||||
|
className="h-5 mr-2 mx-auto text-blue-500 dark:text-blue-400"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function MonitorCard({ key, monitor, data }) {
|
||||||
|
return (
|
||||||
|
<div key={key} className="card">
|
||||||
|
<div className="flex flex-row justify-between items-center mb-2">
|
||||||
|
<div className="flex flex-row items-center align-center">
|
||||||
|
{monitor.description && (
|
||||||
|
<div className="tooltip">
|
||||||
|
{infoIcon}
|
||||||
|
<div className="content text-center transform -translate-y-1/2 top-1/2 ml-8 w-72 text-sm object-left">
|
||||||
|
{monitor.description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="text-xl">{monitor.name}</div>
|
||||||
|
</div>
|
||||||
|
<MonitorStatusLabel kvMonitor={data} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MonitorHistogram monitorId={monitor.id} kvMonitor={data} />
|
||||||
|
|
||||||
|
<div className="flex flex-row justify-between items-center text-gray-400 text-sm">
|
||||||
|
<div>{config.settings.daysInHistogram} days ago</div>
|
||||||
|
<div>Today</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -1,6 +1,21 @@
|
|||||||
import config from '../../config.yaml'
|
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
const searchIcon = (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
className="h-7 mx-auto text-gray-300 dark:text-gray-600"
|
||||||
|
fill="currentColor"
|
||||||
|
>
|
||||||
|
<path d="M9 9a2 2 0 114 0 2 2 0 01-4 0z" />
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-13a4 4 0 00-3.446 6.032l-2.261 2.26a1 1 0 101.414 1.415l2.261-2.261A4 4 0 1011 5z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
export default function MonitorFilter({ active, callback }) {
|
export default function MonitorFilter({ active, callback }) {
|
||||||
const [input, setInput] = useState('')
|
const [input, setInput] = useState('')
|
||||||
|
|
||||||
@ -21,21 +36,19 @@ export default function MonitorFilter({ active, callback }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ui search">
|
<div className="col-span-6 sm:col-span-3 relative">
|
||||||
<div className="ui icon input">
|
<input
|
||||||
<input
|
className="block w-full py-2 px-3 border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 rounded-full shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||||
className="prompt"
|
type="text"
|
||||||
type="text"
|
value={input}
|
||||||
value={input}
|
onInput={handleInput}
|
||||||
onInput={handleInput}
|
onKeyDown={handleKeyDown}
|
||||||
onKeyDown={handleKeyDown}
|
placeholder="Tap '/' to search"
|
||||||
placeholder="Tap '/' to search"
|
tabIndex={0}
|
||||||
tabIndex={0}
|
ref={(e) => e && active && e.focus()}
|
||||||
ref={
|
/>
|
||||||
(e) => e && active && e.focus()
|
<div className="absolute inset-y-1 right-1 flex z-1 items-center">
|
||||||
}
|
{searchIcon}
|
||||||
/>
|
|
||||||
<i className="search icon"></i>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,56 +1,54 @@
|
|||||||
import config from '../../config.yaml'
|
import config from '../../config.yaml'
|
||||||
|
|
||||||
export default function MonitorHistogram({
|
export default function MonitorHistogram({ monitorId, kvMonitor }) {
|
||||||
monitorId,
|
|
||||||
kvMonitor,
|
|
||||||
}) {
|
|
||||||
// create date and set date - daysInHistogram for the first day of the histogram
|
// create date and set date - daysInHistogram for the first day of the histogram
|
||||||
let date = new Date()
|
let date = new Date()
|
||||||
date.setDate(date.getDate() - config.settings.daysInHistogram)
|
date.setDate(date.getDate() - config.settings.daysInHistogram)
|
||||||
|
|
||||||
|
let content = null
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
return (
|
content = Array.from(Array(config.settings.daysInHistogram).keys()).map(
|
||||||
<div
|
(key) => {
|
||||||
key={`${monitorId}-histogram`}
|
date.setDate(date.getDate() + 1)
|
||||||
className="horizontal flex histogram"
|
const dayInHistogram = date.toISOString().split('T')[0]
|
||||||
>
|
|
||||||
{Array.from(Array(config.settings.daysInHistogram).keys()).map(key => {
|
|
||||||
date.setDate(date.getDate() + 1)
|
|
||||||
const dayInHistogram = date.toISOString().split('T')[0]
|
|
||||||
|
|
||||||
let bg = ''
|
let bg = ''
|
||||||
let dayInHistogramLabel = config.settings.dayInHistogramNoData
|
let dayInHistogramLabel = config.settings.dayInHistogramNoData
|
||||||
|
|
||||||
// filter all dates before first check, check the rest
|
// filter all dates before first check, check the rest
|
||||||
if (kvMonitor && kvMonitor.firstCheck <= dayInHistogram) {
|
if (kvMonitor && kvMonitor.firstCheck <= dayInHistogram) {
|
||||||
if (!kvMonitor.failedDays.includes(dayInHistogram)) {
|
if (!kvMonitor.failedDays.includes(dayInHistogram)) {
|
||||||
bg = 'green'
|
bg = 'green'
|
||||||
dayInHistogramLabel = config.settings.dayInHistogramOperational
|
dayInHistogramLabel = config.settings.dayInHistogramOperational
|
||||||
} else {
|
} else {
|
||||||
bg = 'orange'
|
bg = 'yellow'
|
||||||
dayInHistogramLabel = config.settings.dayInHistogramNotOperational
|
dayInHistogramLabel = config.settings.dayInHistogramNotOperational
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={key} className="hitbox">
|
<div key={key} className="hitbox tooltip">
|
||||||
<div
|
<div className={`${bg} bar`} />
|
||||||
className={`${bg} bar`}
|
<div className="content text-center py-1 px-2 mt-2 left-1/2 -ml-20 w-40 text-xs">
|
||||||
data-tooltip={`${dayInHistogram} - ${dayInHistogramLabel}`}
|
{dayInHistogram}
|
||||||
/>
|
<br />
|
||||||
|
<span className="font-semibold text-sm">
|
||||||
|
{dayInHistogramLabel}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
</div>
|
||||||
})}
|
)
|
||||||
</div>
|
},
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={`${monitorId}-histogram`}
|
|
||||||
className="horizontal flex histogram"
|
|
||||||
>
|
|
||||||
<div className="grey-text">Loading histogram ...</div>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`${monitorId}-histogram`}
|
||||||
|
className="flex flex-row items-center histogram"
|
||||||
|
>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,34 @@
|
|||||||
import config from '../../config.yaml'
|
import config from '../../config.yaml'
|
||||||
|
|
||||||
export default function MonitorStatusHeader({kvMonitorsMetadata}) {
|
const classes = {
|
||||||
let backgroundColor = 'green'
|
green:
|
||||||
let headerText = config.settings.allmonitorsOperational
|
'bg-green-200 text-green-700 dark:bg-green-700 dark:text-green-200 border-green-300 dark:border-green-600',
|
||||||
let textColor = 'black'
|
yellow:
|
||||||
|
'bg-yellow-200 text-yellow-700 dark:bg-yellow-700 dark:text-yellow-200 border-yellow-300 dark:border-yellow-600',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MonitorStatusHeader({ kvMonitorsMetadata }) {
|
||||||
|
let color = 'green'
|
||||||
|
let text = config.settings.allmonitorsOperational
|
||||||
|
|
||||||
if (!kvMonitorsMetadata.monitorsOperational) {
|
if (!kvMonitorsMetadata.monitorsOperational) {
|
||||||
backgroundColor = 'yellow'
|
color = 'yellow'
|
||||||
headerText = config.settings.notAllmonitorsOperational
|
text = config.settings.notAllmonitorsOperational
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`ui inverted segment ${backgroundColor}`}>
|
<div className={`card mb-4 font-semibold ${classes[color]}`}>
|
||||||
<div className="horizontal flex between">
|
<div className="flex flex-row justify-between items-center">
|
||||||
<div className={`ui marginless header ${textColor}-text`}>
|
<div>{text}</div>
|
||||||
{headerText}
|
{kvMonitorsMetadata.lastUpdate && typeof window !== 'undefined' && (
|
||||||
</div>
|
<div className="text-xs font-light">
|
||||||
{
|
checked{' '}
|
||||||
kvMonitorsMetadata.lastUpdate && typeof window !== 'undefined' && (
|
{Math.round(
|
||||||
<div className={`${textColor}-text`}>
|
(Date.now() - kvMonitorsMetadata.lastUpdate.time) / 1000,
|
||||||
checked {Math.round((Date.now() - kvMonitorsMetadata.lastUpdate.time) / 1000)} sec ago (from {kvMonitorsMetadata.lastUpdate.loc})
|
)}{' '}
|
||||||
|
sec ago (from {kvMonitorsMetadata.lastUpdate.loc})
|
||||||
</div>
|
</div>
|
||||||
)
|
)}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,18 +1,25 @@
|
|||||||
import config from '../../config.yaml'
|
import config from '../../config.yaml'
|
||||||
|
|
||||||
|
const classes = {
|
||||||
|
gray: 'bg-gray-200 text-gray-800 dark:bg-gray-800 dark:text-gray-200',
|
||||||
|
green: 'bg-green-200 text-green-800 dark:bg-green-800 dark:text-green-200',
|
||||||
|
yellow:
|
||||||
|
'bg-yellow-200 text-yellow-800 dark:bg-yellow-800 dark:text-yellow-200',
|
||||||
|
}
|
||||||
|
|
||||||
export default function MonitorStatusLabel({ kvMonitor }) {
|
export default function MonitorStatusLabel({ kvMonitor }) {
|
||||||
let labelColor = 'grey'
|
let color = 'gray'
|
||||||
let labelText = 'No data'
|
let text = 'No data'
|
||||||
|
|
||||||
if (typeof kvMonitor !== 'undefined') {
|
if (typeof kvMonitor !== 'undefined') {
|
||||||
if (kvMonitor.operational) {
|
if (kvMonitor.operational) {
|
||||||
labelColor = 'green'
|
color = 'green'
|
||||||
labelText = config.settings.monitorLabelOperational
|
text = config.settings.monitorLabelOperational
|
||||||
} else {
|
} else {
|
||||||
labelColor = 'orange'
|
color = 'yellow'
|
||||||
labelText = config.settings.monitorLabelNotOperational
|
text = config.settings.monitorLabelNotOperational
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={`ui ${labelColor} horizontal label`}>{labelText}</div>
|
return <div className={`pill leading-5 ${classes[color]}`}>{text}</div>
|
||||||
}
|
}
|
||||||
|
58
src/components/themeSwitcher.js
Normal file
58
src/components/themeSwitcher.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
const moonIcon = (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
className="h-5 mx-auto"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
const sunIcon = (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
className="h-5 mx-auto"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function ThemeSwitcher() {
|
||||||
|
const [darkmode, setDark] = useState(localStorage.getItem('theme') === 'dark')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTheme(darkmode ? 'dark' : 'light')
|
||||||
|
}, [darkmode])
|
||||||
|
|
||||||
|
const changeTheme = () => {
|
||||||
|
setDark(!darkmode)
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonColor = darkmode ? 'bg-gray-700' : 'bg-gray-200'
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={`${buttonColor} rounded-full h-7 w-7 mr-4`}
|
||||||
|
onClick={changeTheme}
|
||||||
|
>
|
||||||
|
{darkmode ? sunIcon : moonIcon}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
@ -1,10 +1,6 @@
|
|||||||
import config from '../../config.yaml'
|
import config from '../../config.yaml'
|
||||||
|
|
||||||
import {
|
import { setKV, getKVWithMetadata, notifySlack } from './helpers'
|
||||||
setKV,
|
|
||||||
getKVWithMetadata,
|
|
||||||
notifySlack,
|
|
||||||
} from './helpers'
|
|
||||||
|
|
||||||
function getDate() {
|
function getDate() {
|
||||||
return new Date().toISOString().split('T')[0]
|
return new Date().toISOString().split('T')[0]
|
||||||
@ -12,7 +8,10 @@ function getDate() {
|
|||||||
|
|
||||||
export async function processCronTrigger(event) {
|
export async function processCronTrigger(event) {
|
||||||
// Get monitors state from KV
|
// Get monitors state from KV
|
||||||
let {value: monitorsState, metadata: monitorsStateMetadata} = await getKVWithMetadata('monitors_data', 'json')
|
let {
|
||||||
|
value: monitorsState,
|
||||||
|
metadata: monitorsStateMetadata,
|
||||||
|
} = await getKVWithMetadata('monitors_data', 'json')
|
||||||
|
|
||||||
// Create empty state objects if not exists in KV storage yet
|
// Create empty state objects if not exists in KV storage yet
|
||||||
if (!monitorsState) {
|
if (!monitorsState) {
|
||||||
@ -28,7 +27,7 @@ export async function processCronTrigger(event) {
|
|||||||
for (const monitor of config.monitors) {
|
for (const monitor of config.monitors) {
|
||||||
// Create default monitor state if does not exist yet
|
// Create default monitor state if does not exist yet
|
||||||
if (typeof monitorsState[monitor.id] === 'undefined') {
|
if (typeof monitorsState[monitor.id] === 'undefined') {
|
||||||
monitorsState[monitor.id] = {failedDays: []}
|
monitorsState[monitor.id] = { failedDays: [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Checking ${monitor.name} ...`)
|
console.log(`Checking ${monitor.name} ...`)
|
||||||
@ -43,15 +42,22 @@ export async function processCronTrigger(event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const checkResponse = await fetch(monitor.url, init)
|
const checkResponse = await fetch(monitor.url, init)
|
||||||
const monitorOperational = checkResponse.status === (monitor.expectStatus || 200)
|
const monitorOperational =
|
||||||
|
checkResponse.status === (monitor.expectStatus || 200)
|
||||||
|
|
||||||
// Send Slack message on monitor change
|
// Send Slack message on monitor change
|
||||||
if (monitorsState[monitor.id].operational !== monitorOperational && typeof SECRET_SLACK_WEBHOOK_URL !== 'undefined' && SECRET_SLACK_WEBHOOK_URL !== 'default-gh-action-secret') {
|
if (
|
||||||
event.waitUntil(notifySlack(monitor, monitorOperational))
|
monitorsState[monitor.id].operational !== monitorOperational &&
|
||||||
|
typeof SECRET_SLACK_WEBHOOK_URL !== 'undefined' &&
|
||||||
|
SECRET_SLACK_WEBHOOK_URL !== 'default-gh-action-secret'
|
||||||
|
) {
|
||||||
|
event.waitUntil(notifySlack(monitor, monitorOperational))
|
||||||
}
|
}
|
||||||
|
|
||||||
monitorsState[monitor.id].operational = checkResponse.status === (monitor.expectStatus || 200)
|
monitorsState[monitor.id].operational =
|
||||||
monitorsState[monitor.id].firstCheck = monitorsState[monitor.id].firstCheck || getDate()
|
checkResponse.status === (monitor.expectStatus || 200)
|
||||||
|
monitorsState[monitor.id].firstCheck =
|
||||||
|
monitorsState[monitor.id].firstCheck || getDate()
|
||||||
|
|
||||||
// Set monitorsOperational and push current day to failedDays
|
// Set monitorsOperational and push current day to failedDays
|
||||||
if (!monitorOperational) {
|
if (!monitorOperational) {
|
||||||
@ -72,11 +78,15 @@ export async function processCronTrigger(event) {
|
|||||||
const loc = res.headers.get('cf-ray').split('-')[1]
|
const loc = res.headers.get('cf-ray').split('-')[1]
|
||||||
monitorsStateMetadata.lastUpdate = {
|
monitorsStateMetadata.lastUpdate = {
|
||||||
loc,
|
loc,
|
||||||
time: Date.now()
|
time: Date.now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save monitorsState and monitorsStateMetadata to KV storage
|
// Save monitorsState and monitorsStateMetadata to KV storage
|
||||||
await setKV('monitors_data', JSON.stringify(monitorsState), monitorsStateMetadata)
|
await setKV(
|
||||||
|
'monitors_data',
|
||||||
|
JSON.stringify(monitorsState),
|
||||||
|
monitorsStateMetadata,
|
||||||
|
)
|
||||||
|
|
||||||
return new Response('OK')
|
return new Response('OK')
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import config from '../../config.yaml'
|
import config from '../../config.yaml'
|
||||||
import {useEffect, useState} from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
export async function getMonitors() {
|
export async function getMonitors() {
|
||||||
return await getKVWithMetadata('monitors_data', "json")
|
return await getKVWithMetadata('monitors_data', 'json')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setKV(key, value, metadata, expirationTtl) {
|
export async function setKV(key, value, metadata, expirationTtl) {
|
||||||
@ -23,11 +23,10 @@ export async function notifySlack(monitor, operational) {
|
|||||||
type: 'section',
|
type: 'section',
|
||||||
text: {
|
text: {
|
||||||
type: 'mrkdwn',
|
type: 'mrkdwn',
|
||||||
text: `Monitor *${monitor.name}* changed status to *${
|
text: `Monitor *${monitor.name}* changed status to *${operational
|
||||||
operational
|
|
||||||
? config.settings.monitorLabelOperational
|
? config.settings.monitorLabelOperational
|
||||||
: config.settings.monitorLabelNotOperational
|
: config.settings.monitorLabelNotOperational
|
||||||
}*`,
|
}*`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -35,11 +34,9 @@ export async function notifySlack(monitor, operational) {
|
|||||||
elements: [
|
elements: [
|
||||||
{
|
{
|
||||||
type: 'mrkdwn',
|
type: 'mrkdwn',
|
||||||
text: `${
|
text: `${operational ? ':white_check_mark:' : ':x:'} \`${monitor.method ? monitor.method : 'GET'
|
||||||
operational ? ':white_check_mark:' : ':x:'
|
} ${monitor.url}\` - :eyes: <${config.settings.url
|
||||||
} \`${monitor.method ? monitor.method : "GET"} ${monitor.url}\` - :eyes: <${
|
}|Status Page>`,
|
||||||
config.settings.url
|
|
||||||
}|Status Page>`,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -59,24 +56,24 @@ export function useKeyPress(targetKey) {
|
|||||||
|
|
||||||
function downHandler({ key }) {
|
function downHandler({ key }) {
|
||||||
if (key === targetKey) {
|
if (key === targetKey) {
|
||||||
setKeyPressed(true);
|
setKeyPressed(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const upHandler = ({ key }) => {
|
const upHandler = ({ key }) => {
|
||||||
if (key === targetKey) {
|
if (key === targetKey) {
|
||||||
setKeyPressed(false);
|
setKeyPressed(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.addEventListener('keydown', downHandler);
|
window.addEventListener('keydown', downHandler)
|
||||||
window.addEventListener('keyup', upHandler);
|
window.addEventListener('keyup', upHandler)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('keydown', downHandler);
|
window.removeEventListener('keydown', downHandler)
|
||||||
window.removeEventListener('keyup', upHandler);
|
window.removeEventListener('keyup', upHandler)
|
||||||
};
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return keyPressed
|
return keyPressed
|
||||||
|
918
tailwind.config.js
Normal file
918
tailwind.config.js
Normal file
@ -0,0 +1,918 @@
|
|||||||
|
const colors = require('tailwindcss/colors')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
purge: {
|
||||||
|
content: ['./src/**/*.js', './pages/**/*.js', './public/tailwind.css'],
|
||||||
|
},
|
||||||
|
presets: [],
|
||||||
|
darkMode: 'class', // or 'media' or 'class'
|
||||||
|
theme: {
|
||||||
|
screens: {
|
||||||
|
sm: '640px',
|
||||||
|
md: '768px',
|
||||||
|
lg: '1024px',
|
||||||
|
xl: '1280px',
|
||||||
|
'2xl': '1536px',
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
transparent: 'transparent',
|
||||||
|
current: 'currentColor',
|
||||||
|
|
||||||
|
black: colors.black,
|
||||||
|
white: colors.white,
|
||||||
|
gray: colors.coolGray,
|
||||||
|
red: colors.red,
|
||||||
|
yellow: colors.yellow,
|
||||||
|
green: colors.green,
|
||||||
|
blue: colors.lightBlue,
|
||||||
|
indigo: colors.indigo,
|
||||||
|
purple: colors.violet,
|
||||||
|
pink: colors.pink,
|
||||||
|
},
|
||||||
|
spacing: {
|
||||||
|
px: '1px',
|
||||||
|
0: '0px',
|
||||||
|
0.5: '0.125rem',
|
||||||
|
1: '0.25rem',
|
||||||
|
1.5: '0.375rem',
|
||||||
|
2: '0.5rem',
|
||||||
|
2.5: '0.625rem',
|
||||||
|
3: '0.75rem',
|
||||||
|
3.5: '0.875rem',
|
||||||
|
4: '1rem',
|
||||||
|
5: '1.25rem',
|
||||||
|
6: '1.5rem',
|
||||||
|
7: '1.75rem',
|
||||||
|
8: '2rem',
|
||||||
|
9: '2.25rem',
|
||||||
|
10: '2.5rem',
|
||||||
|
11: '2.75rem',
|
||||||
|
12: '3rem',
|
||||||
|
14: '3.5rem',
|
||||||
|
16: '4rem',
|
||||||
|
20: '5rem',
|
||||||
|
24: '6rem',
|
||||||
|
28: '7rem',
|
||||||
|
32: '8rem',
|
||||||
|
36: '9rem',
|
||||||
|
40: '10rem',
|
||||||
|
44: '11rem',
|
||||||
|
48: '12rem',
|
||||||
|
52: '13rem',
|
||||||
|
56: '14rem',
|
||||||
|
60: '15rem',
|
||||||
|
64: '16rem',
|
||||||
|
72: '18rem',
|
||||||
|
80: '20rem',
|
||||||
|
96: '24rem',
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
none: 'none',
|
||||||
|
spin: 'spin 1s linear infinite',
|
||||||
|
ping: 'ping 1s cubic-bezier(0, 0, 0.2, 1) infinite',
|
||||||
|
pulse: 'pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite',
|
||||||
|
bounce: 'bounce 1s infinite',
|
||||||
|
},
|
||||||
|
backgroundColor: (theme) => theme('colors'),
|
||||||
|
backgroundImage: {
|
||||||
|
none: 'none',
|
||||||
|
'gradient-to-t': 'linear-gradient(to top, var(--tw-gradient-stops))',
|
||||||
|
'gradient-to-tr':
|
||||||
|
'linear-gradient(to top right, var(--tw-gradient-stops))',
|
||||||
|
'gradient-to-r': 'linear-gradient(to right, var(--tw-gradient-stops))',
|
||||||
|
'gradient-to-br':
|
||||||
|
'linear-gradient(to bottom right, var(--tw-gradient-stops))',
|
||||||
|
'gradient-to-b': 'linear-gradient(to bottom, var(--tw-gradient-stops))',
|
||||||
|
'gradient-to-bl':
|
||||||
|
'linear-gradient(to bottom left, var(--tw-gradient-stops))',
|
||||||
|
'gradient-to-l': 'linear-gradient(to left, var(--tw-gradient-stops))',
|
||||||
|
'gradient-to-tl':
|
||||||
|
'linear-gradient(to top left, var(--tw-gradient-stops))',
|
||||||
|
},
|
||||||
|
backgroundOpacity: (theme) => theme('opacity'),
|
||||||
|
backgroundPosition: {
|
||||||
|
bottom: 'bottom',
|
||||||
|
center: 'center',
|
||||||
|
left: 'left',
|
||||||
|
'left-bottom': 'left bottom',
|
||||||
|
'left-top': 'left top',
|
||||||
|
right: 'right',
|
||||||
|
'right-bottom': 'right bottom',
|
||||||
|
'right-top': 'right top',
|
||||||
|
top: 'top',
|
||||||
|
},
|
||||||
|
backgroundSize: {
|
||||||
|
auto: 'auto',
|
||||||
|
cover: 'cover',
|
||||||
|
contain: 'contain',
|
||||||
|
},
|
||||||
|
borderColor: (theme) => ({
|
||||||
|
...theme('colors'),
|
||||||
|
DEFAULT: theme('colors.gray.200', 'currentColor'),
|
||||||
|
}),
|
||||||
|
borderOpacity: (theme) => theme('opacity'),
|
||||||
|
borderRadius: {
|
||||||
|
none: '0px',
|
||||||
|
sm: '0.125rem',
|
||||||
|
DEFAULT: '0.25rem',
|
||||||
|
md: '0.375rem',
|
||||||
|
lg: '0.5rem',
|
||||||
|
xl: '0.75rem',
|
||||||
|
'2xl': '1rem',
|
||||||
|
'3xl': '1.5rem',
|
||||||
|
full: '9999px',
|
||||||
|
},
|
||||||
|
borderWidth: {
|
||||||
|
DEFAULT: '1px',
|
||||||
|
0: '0px',
|
||||||
|
2: '2px',
|
||||||
|
4: '4px',
|
||||||
|
8: '8px',
|
||||||
|
},
|
||||||
|
boxShadow: {
|
||||||
|
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
|
||||||
|
DEFAULT:
|
||||||
|
'0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
|
||||||
|
md:
|
||||||
|
'0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
|
||||||
|
lg:
|
||||||
|
'0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
|
||||||
|
xl:
|
||||||
|
'0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
|
||||||
|
'2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
|
||||||
|
inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',
|
||||||
|
none: 'none',
|
||||||
|
},
|
||||||
|
container: {},
|
||||||
|
cursor: {
|
||||||
|
auto: 'auto',
|
||||||
|
default: 'default',
|
||||||
|
pointer: 'pointer',
|
||||||
|
wait: 'wait',
|
||||||
|
text: 'text',
|
||||||
|
move: 'move',
|
||||||
|
'not-allowed': 'not-allowed',
|
||||||
|
},
|
||||||
|
divideColor: (theme) => theme('borderColor'),
|
||||||
|
divideOpacity: (theme) => theme('borderOpacity'),
|
||||||
|
divideWidth: (theme) => theme('borderWidth'),
|
||||||
|
fill: { current: 'currentColor' },
|
||||||
|
flex: {
|
||||||
|
1: '1 1 0%',
|
||||||
|
auto: '1 1 auto',
|
||||||
|
initial: '0 1 auto',
|
||||||
|
none: 'none',
|
||||||
|
},
|
||||||
|
flexGrow: {
|
||||||
|
0: '0',
|
||||||
|
DEFAULT: '1',
|
||||||
|
},
|
||||||
|
flexShrink: {
|
||||||
|
0: '0',
|
||||||
|
DEFAULT: '1',
|
||||||
|
},
|
||||||
|
fontFamily: {
|
||||||
|
sans: [
|
||||||
|
'ui-sans-serif',
|
||||||
|
'system-ui',
|
||||||
|
'-apple-system',
|
||||||
|
'BlinkMacSystemFont',
|
||||||
|
'"Segoe UI"',
|
||||||
|
'Roboto',
|
||||||
|
'"Helvetica Neue"',
|
||||||
|
'Arial',
|
||||||
|
'"Noto Sans"',
|
||||||
|
'sans-serif',
|
||||||
|
'"Apple Color Emoji"',
|
||||||
|
'"Segoe UI Emoji"',
|
||||||
|
'"Segoe UI Symbol"',
|
||||||
|
'"Noto Color Emoji"',
|
||||||
|
],
|
||||||
|
serif: [
|
||||||
|
'ui-serif',
|
||||||
|
'Georgia',
|
||||||
|
'Cambria',
|
||||||
|
'"Times New Roman"',
|
||||||
|
'Times',
|
||||||
|
'serif',
|
||||||
|
],
|
||||||
|
mono: [
|
||||||
|
'ui-monospace',
|
||||||
|
'SFMono-Regular',
|
||||||
|
'Menlo',
|
||||||
|
'Monaco',
|
||||||
|
'Consolas',
|
||||||
|
'"Liberation Mono"',
|
||||||
|
'"Courier New"',
|
||||||
|
'monospace',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
fontSize: {
|
||||||
|
xs: ['0.75rem', { lineHeight: '1rem' }],
|
||||||
|
sm: ['0.875rem', { lineHeight: '1.25rem' }],
|
||||||
|
base: ['1rem', { lineHeight: '1.5rem' }],
|
||||||
|
lg: ['1.125rem', { lineHeight: '1.75rem' }],
|
||||||
|
xl: ['1.25rem', { lineHeight: '1.75rem' }],
|
||||||
|
'2xl': ['1.5rem', { lineHeight: '2rem' }],
|
||||||
|
'3xl': ['1.875rem', { lineHeight: '2.25rem' }],
|
||||||
|
'4xl': ['2.25rem', { lineHeight: '2.5rem' }],
|
||||||
|
'5xl': ['3rem', { lineHeight: '1' }],
|
||||||
|
'6xl': ['3.75rem', { lineHeight: '1' }],
|
||||||
|
'7xl': ['4.5rem', { lineHeight: '1' }],
|
||||||
|
'8xl': ['6rem', { lineHeight: '1' }],
|
||||||
|
'9xl': ['8rem', { lineHeight: '1' }],
|
||||||
|
},
|
||||||
|
fontWeight: {
|
||||||
|
thin: '100',
|
||||||
|
extralight: '200',
|
||||||
|
light: '300',
|
||||||
|
normal: '400',
|
||||||
|
medium: '500',
|
||||||
|
semibold: '600',
|
||||||
|
bold: '700',
|
||||||
|
extrabold: '800',
|
||||||
|
black: '900',
|
||||||
|
},
|
||||||
|
gap: (theme) => theme('spacing'),
|
||||||
|
gradientColorStops: (theme) => theme('colors'),
|
||||||
|
gridAutoColumns: {
|
||||||
|
auto: 'auto',
|
||||||
|
min: 'min-content',
|
||||||
|
max: 'max-content',
|
||||||
|
fr: 'minmax(0, 1fr)',
|
||||||
|
},
|
||||||
|
gridAutoRows: {
|
||||||
|
auto: 'auto',
|
||||||
|
min: 'min-content',
|
||||||
|
max: 'max-content',
|
||||||
|
fr: 'minmax(0, 1fr)',
|
||||||
|
},
|
||||||
|
gridColumn: {
|
||||||
|
auto: 'auto',
|
||||||
|
'span-1': 'span 1 / span 1',
|
||||||
|
'span-2': 'span 2 / span 2',
|
||||||
|
'span-3': 'span 3 / span 3',
|
||||||
|
'span-4': 'span 4 / span 4',
|
||||||
|
'span-5': 'span 5 / span 5',
|
||||||
|
'span-6': 'span 6 / span 6',
|
||||||
|
'span-7': 'span 7 / span 7',
|
||||||
|
'span-8': 'span 8 / span 8',
|
||||||
|
'span-9': 'span 9 / span 9',
|
||||||
|
'span-10': 'span 10 / span 10',
|
||||||
|
'span-11': 'span 11 / span 11',
|
||||||
|
'span-12': 'span 12 / span 12',
|
||||||
|
'span-full': '1 / -1',
|
||||||
|
},
|
||||||
|
gridColumnEnd: {
|
||||||
|
auto: 'auto',
|
||||||
|
1: '1',
|
||||||
|
2: '2',
|
||||||
|
3: '3',
|
||||||
|
4: '4',
|
||||||
|
5: '5',
|
||||||
|
6: '6',
|
||||||
|
7: '7',
|
||||||
|
8: '8',
|
||||||
|
9: '9',
|
||||||
|
10: '10',
|
||||||
|
11: '11',
|
||||||
|
12: '12',
|
||||||
|
13: '13',
|
||||||
|
},
|
||||||
|
gridColumnStart: {
|
||||||
|
auto: 'auto',
|
||||||
|
1: '1',
|
||||||
|
2: '2',
|
||||||
|
3: '3',
|
||||||
|
4: '4',
|
||||||
|
5: '5',
|
||||||
|
6: '6',
|
||||||
|
7: '7',
|
||||||
|
8: '8',
|
||||||
|
9: '9',
|
||||||
|
10: '10',
|
||||||
|
11: '11',
|
||||||
|
12: '12',
|
||||||
|
13: '13',
|
||||||
|
},
|
||||||
|
gridRow: {
|
||||||
|
auto: 'auto',
|
||||||
|
'span-1': 'span 1 / span 1',
|
||||||
|
'span-2': 'span 2 / span 2',
|
||||||
|
'span-3': 'span 3 / span 3',
|
||||||
|
'span-4': 'span 4 / span 4',
|
||||||
|
'span-5': 'span 5 / span 5',
|
||||||
|
'span-6': 'span 6 / span 6',
|
||||||
|
'span-full': '1 / -1',
|
||||||
|
},
|
||||||
|
gridRowStart: {
|
||||||
|
auto: 'auto',
|
||||||
|
1: '1',
|
||||||
|
2: '2',
|
||||||
|
3: '3',
|
||||||
|
4: '4',
|
||||||
|
5: '5',
|
||||||
|
6: '6',
|
||||||
|
7: '7',
|
||||||
|
},
|
||||||
|
gridRowEnd: {
|
||||||
|
auto: 'auto',
|
||||||
|
1: '1',
|
||||||
|
2: '2',
|
||||||
|
3: '3',
|
||||||
|
4: '4',
|
||||||
|
5: '5',
|
||||||
|
6: '6',
|
||||||
|
7: '7',
|
||||||
|
},
|
||||||
|
transformOrigin: {
|
||||||
|
center: 'center',
|
||||||
|
top: 'top',
|
||||||
|
'top-right': 'top right',
|
||||||
|
right: 'right',
|
||||||
|
'bottom-right': 'bottom right',
|
||||||
|
bottom: 'bottom',
|
||||||
|
'bottom-left': 'bottom left',
|
||||||
|
left: 'left',
|
||||||
|
'top-left': 'top left',
|
||||||
|
},
|
||||||
|
gridTemplateColumns: {
|
||||||
|
none: 'none',
|
||||||
|
1: 'repeat(1, minmax(0, 1fr))',
|
||||||
|
2: 'repeat(2, minmax(0, 1fr))',
|
||||||
|
3: 'repeat(3, minmax(0, 1fr))',
|
||||||
|
4: 'repeat(4, minmax(0, 1fr))',
|
||||||
|
5: 'repeat(5, minmax(0, 1fr))',
|
||||||
|
6: 'repeat(6, minmax(0, 1fr))',
|
||||||
|
7: 'repeat(7, minmax(0, 1fr))',
|
||||||
|
8: 'repeat(8, minmax(0, 1fr))',
|
||||||
|
9: 'repeat(9, minmax(0, 1fr))',
|
||||||
|
10: 'repeat(10, minmax(0, 1fr))',
|
||||||
|
11: 'repeat(11, minmax(0, 1fr))',
|
||||||
|
12: 'repeat(12, minmax(0, 1fr))',
|
||||||
|
},
|
||||||
|
gridTemplateRows: {
|
||||||
|
none: 'none',
|
||||||
|
1: 'repeat(1, minmax(0, 1fr))',
|
||||||
|
2: 'repeat(2, minmax(0, 1fr))',
|
||||||
|
3: 'repeat(3, minmax(0, 1fr))',
|
||||||
|
4: 'repeat(4, minmax(0, 1fr))',
|
||||||
|
5: 'repeat(5, minmax(0, 1fr))',
|
||||||
|
6: 'repeat(6, minmax(0, 1fr))',
|
||||||
|
},
|
||||||
|
height: (theme) => ({
|
||||||
|
auto: 'auto',
|
||||||
|
...theme('spacing'),
|
||||||
|
'1/2': '50%',
|
||||||
|
'1/3': '33.333333%',
|
||||||
|
'2/3': '66.666667%',
|
||||||
|
'1/4': '25%',
|
||||||
|
'2/4': '50%',
|
||||||
|
'3/4': '75%',
|
||||||
|
'1/5': '20%',
|
||||||
|
'2/5': '40%',
|
||||||
|
'3/5': '60%',
|
||||||
|
'4/5': '80%',
|
||||||
|
'1/6': '16.666667%',
|
||||||
|
'2/6': '33.333333%',
|
||||||
|
'3/6': '50%',
|
||||||
|
'4/6': '66.666667%',
|
||||||
|
'5/6': '83.333333%',
|
||||||
|
full: '100%',
|
||||||
|
screen: '100vh',
|
||||||
|
}),
|
||||||
|
inset: (theme, { negative }) => ({
|
||||||
|
auto: 'auto',
|
||||||
|
...theme('spacing'),
|
||||||
|
...negative(theme('spacing')),
|
||||||
|
'1/2': '50%',
|
||||||
|
'1/3': '33.333333%',
|
||||||
|
'2/3': '66.666667%',
|
||||||
|
'1/4': '25%',
|
||||||
|
'2/4': '50%',
|
||||||
|
'3/4': '75%',
|
||||||
|
full: '100%',
|
||||||
|
'-1/2': '-50%',
|
||||||
|
'-1/3': '-33.333333%',
|
||||||
|
'-2/3': '-66.666667%',
|
||||||
|
'-1/4': '-25%',
|
||||||
|
'-2/4': '-50%',
|
||||||
|
'-3/4': '-75%',
|
||||||
|
'-full': '-100%',
|
||||||
|
}),
|
||||||
|
keyframes: {
|
||||||
|
spin: {
|
||||||
|
to: {
|
||||||
|
transform: 'rotate(360deg)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ping: {
|
||||||
|
'75%, 100%': {
|
||||||
|
transform: 'scale(2)',
|
||||||
|
opacity: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pulse: {
|
||||||
|
'50%': {
|
||||||
|
opacity: '.5',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bounce: {
|
||||||
|
'0%, 100%': {
|
||||||
|
transform: 'translateY(-25%)',
|
||||||
|
animationTimingFunction: 'cubic-bezier(0.8,0,1,1)',
|
||||||
|
},
|
||||||
|
'50%': {
|
||||||
|
transform: 'none',
|
||||||
|
animationTimingFunction: 'cubic-bezier(0,0,0.2,1)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
letterSpacing: {
|
||||||
|
tighter: '-0.05em',
|
||||||
|
tight: '-0.025em',
|
||||||
|
normal: '0em',
|
||||||
|
wide: '0.025em',
|
||||||
|
wider: '0.05em',
|
||||||
|
widest: '0.1em',
|
||||||
|
},
|
||||||
|
lineHeight: {
|
||||||
|
none: '1',
|
||||||
|
tight: '1.25',
|
||||||
|
snug: '1.375',
|
||||||
|
normal: '1.5',
|
||||||
|
relaxed: '1.625',
|
||||||
|
loose: '2',
|
||||||
|
3: '.75rem',
|
||||||
|
4: '1rem',
|
||||||
|
5: '1.25rem',
|
||||||
|
6: '1.5rem',
|
||||||
|
7: '1.75rem',
|
||||||
|
8: '2rem',
|
||||||
|
9: '2.25rem',
|
||||||
|
10: '2.5rem',
|
||||||
|
},
|
||||||
|
listStyleType: {
|
||||||
|
none: 'none',
|
||||||
|
disc: 'disc',
|
||||||
|
decimal: 'decimal',
|
||||||
|
},
|
||||||
|
margin: (theme, { negative }) => ({
|
||||||
|
auto: 'auto',
|
||||||
|
...theme('spacing'),
|
||||||
|
...negative(theme('spacing')),
|
||||||
|
}),
|
||||||
|
maxHeight: (theme) => ({
|
||||||
|
...theme('spacing'),
|
||||||
|
full: '100%',
|
||||||
|
screen: '100vh',
|
||||||
|
}),
|
||||||
|
maxWidth: (theme, { breakpoints }) => ({
|
||||||
|
none: 'none',
|
||||||
|
0: '0rem',
|
||||||
|
xs: '20rem',
|
||||||
|
sm: '24rem',
|
||||||
|
md: '28rem',
|
||||||
|
lg: '32rem',
|
||||||
|
xl: '36rem',
|
||||||
|
'2xl': '42rem',
|
||||||
|
'3xl': '48rem',
|
||||||
|
'4xl': '56rem',
|
||||||
|
'5xl': '64rem',
|
||||||
|
'6xl': '72rem',
|
||||||
|
'7xl': '80rem',
|
||||||
|
full: '100%',
|
||||||
|
min: 'min-content',
|
||||||
|
max: 'max-content',
|
||||||
|
prose: '65ch',
|
||||||
|
...breakpoints(theme('screens')),
|
||||||
|
}),
|
||||||
|
minHeight: {
|
||||||
|
0: '0px',
|
||||||
|
full: '100%',
|
||||||
|
screen: '100vh',
|
||||||
|
},
|
||||||
|
minWidth: {
|
||||||
|
0: '0px',
|
||||||
|
full: '100%',
|
||||||
|
min: 'min-content',
|
||||||
|
max: 'max-content',
|
||||||
|
},
|
||||||
|
objectPosition: {
|
||||||
|
bottom: 'bottom',
|
||||||
|
center: 'center',
|
||||||
|
left: 'left',
|
||||||
|
'left-bottom': 'left bottom',
|
||||||
|
'left-top': 'left top',
|
||||||
|
right: 'right',
|
||||||
|
'right-bottom': 'right bottom',
|
||||||
|
'right-top': 'right top',
|
||||||
|
top: 'top',
|
||||||
|
},
|
||||||
|
opacity: {
|
||||||
|
0: '0',
|
||||||
|
5: '0.05',
|
||||||
|
10: '0.1',
|
||||||
|
20: '0.2',
|
||||||
|
25: '0.25',
|
||||||
|
30: '0.3',
|
||||||
|
40: '0.4',
|
||||||
|
50: '0.5',
|
||||||
|
60: '0.6',
|
||||||
|
70: '0.7',
|
||||||
|
75: '0.75',
|
||||||
|
80: '0.8',
|
||||||
|
90: '0.9',
|
||||||
|
95: '0.95',
|
||||||
|
100: '1',
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
first: '-9999',
|
||||||
|
last: '9999',
|
||||||
|
none: '0',
|
||||||
|
1: '1',
|
||||||
|
2: '2',
|
||||||
|
3: '3',
|
||||||
|
4: '4',
|
||||||
|
5: '5',
|
||||||
|
6: '6',
|
||||||
|
7: '7',
|
||||||
|
8: '8',
|
||||||
|
9: '9',
|
||||||
|
10: '10',
|
||||||
|
11: '11',
|
||||||
|
12: '12',
|
||||||
|
},
|
||||||
|
outline: {
|
||||||
|
none: ['2px solid transparent', '2px'],
|
||||||
|
white: ['2px dotted white', '2px'],
|
||||||
|
black: ['2px dotted black', '2px'],
|
||||||
|
},
|
||||||
|
padding: (theme) => theme('spacing'),
|
||||||
|
placeholderColor: (theme) => theme('colors'),
|
||||||
|
placeholderOpacity: (theme) => theme('opacity'),
|
||||||
|
ringColor: (theme) => ({
|
||||||
|
DEFAULT: theme('colors.blue.500', '#3b82f6'),
|
||||||
|
...theme('colors'),
|
||||||
|
}),
|
||||||
|
ringOffsetColor: (theme) => theme('colors'),
|
||||||
|
ringOffsetWidth: {
|
||||||
|
0: '0px',
|
||||||
|
1: '1px',
|
||||||
|
2: '2px',
|
||||||
|
4: '4px',
|
||||||
|
8: '8px',
|
||||||
|
},
|
||||||
|
ringOpacity: (theme) => ({
|
||||||
|
DEFAULT: '0.5',
|
||||||
|
...theme('opacity'),
|
||||||
|
}),
|
||||||
|
ringWidth: {
|
||||||
|
DEFAULT: '3px',
|
||||||
|
0: '0px',
|
||||||
|
1: '1px',
|
||||||
|
2: '2px',
|
||||||
|
4: '4px',
|
||||||
|
8: '8px',
|
||||||
|
},
|
||||||
|
rotate: {
|
||||||
|
'-180': '-180deg',
|
||||||
|
'-90': '-90deg',
|
||||||
|
'-45': '-45deg',
|
||||||
|
'-12': '-12deg',
|
||||||
|
'-6': '-6deg',
|
||||||
|
'-3': '-3deg',
|
||||||
|
'-2': '-2deg',
|
||||||
|
'-1': '-1deg',
|
||||||
|
0: '0deg',
|
||||||
|
1: '1deg',
|
||||||
|
2: '2deg',
|
||||||
|
3: '3deg',
|
||||||
|
6: '6deg',
|
||||||
|
12: '12deg',
|
||||||
|
45: '45deg',
|
||||||
|
90: '90deg',
|
||||||
|
180: '180deg',
|
||||||
|
},
|
||||||
|
scale: {
|
||||||
|
0: '0',
|
||||||
|
50: '.5',
|
||||||
|
75: '.75',
|
||||||
|
90: '.9',
|
||||||
|
95: '.95',
|
||||||
|
100: '1',
|
||||||
|
105: '1.05',
|
||||||
|
110: '1.1',
|
||||||
|
125: '1.25',
|
||||||
|
150: '1.5',
|
||||||
|
},
|
||||||
|
skew: {
|
||||||
|
'-12': '-12deg',
|
||||||
|
'-6': '-6deg',
|
||||||
|
'-3': '-3deg',
|
||||||
|
'-2': '-2deg',
|
||||||
|
'-1': '-1deg',
|
||||||
|
0: '0deg',
|
||||||
|
1: '1deg',
|
||||||
|
2: '2deg',
|
||||||
|
3: '3deg',
|
||||||
|
6: '6deg',
|
||||||
|
12: '12deg',
|
||||||
|
},
|
||||||
|
space: (theme, { negative }) => ({
|
||||||
|
...theme('spacing'),
|
||||||
|
...negative(theme('spacing')),
|
||||||
|
}),
|
||||||
|
stroke: {
|
||||||
|
current: 'currentColor',
|
||||||
|
},
|
||||||
|
strokeWidth: {
|
||||||
|
0: '0',
|
||||||
|
1: '1',
|
||||||
|
2: '2',
|
||||||
|
},
|
||||||
|
textColor: (theme) => theme('colors'),
|
||||||
|
textOpacity: (theme) => theme('opacity'),
|
||||||
|
transitionDuration: {
|
||||||
|
DEFAULT: '150ms',
|
||||||
|
75: '75ms',
|
||||||
|
100: '100ms',
|
||||||
|
150: '150ms',
|
||||||
|
200: '200ms',
|
||||||
|
300: '300ms',
|
||||||
|
500: '500ms',
|
||||||
|
700: '700ms',
|
||||||
|
1000: '1000ms',
|
||||||
|
},
|
||||||
|
transitionDelay: {
|
||||||
|
75: '75ms',
|
||||||
|
100: '100ms',
|
||||||
|
150: '150ms',
|
||||||
|
200: '200ms',
|
||||||
|
300: '300ms',
|
||||||
|
500: '500ms',
|
||||||
|
700: '700ms',
|
||||||
|
1000: '1000ms',
|
||||||
|
},
|
||||||
|
transitionProperty: {
|
||||||
|
none: 'none',
|
||||||
|
all: 'all',
|
||||||
|
DEFAULT:
|
||||||
|
'background-color, border-color, color, fill, stroke, opacity, box-shadow, transform',
|
||||||
|
colors: 'background-color, border-color, color, fill, stroke',
|
||||||
|
opacity: 'opacity',
|
||||||
|
shadow: 'box-shadow',
|
||||||
|
transform: 'transform',
|
||||||
|
},
|
||||||
|
transitionTimingFunction: {
|
||||||
|
DEFAULT: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
linear: 'linear',
|
||||||
|
in: 'cubic-bezier(0.4, 0, 1, 1)',
|
||||||
|
out: 'cubic-bezier(0, 0, 0.2, 1)',
|
||||||
|
'in-out': 'cubic-bezier(0.4, 0, 0.2, 1)',
|
||||||
|
},
|
||||||
|
translate: (theme, { negative }) => ({
|
||||||
|
...theme('spacing'),
|
||||||
|
...negative(theme('spacing')),
|
||||||
|
'1/2': '50%',
|
||||||
|
'1/3': '33.333333%',
|
||||||
|
'2/3': '66.666667%',
|
||||||
|
'1/4': '25%',
|
||||||
|
'2/4': '50%',
|
||||||
|
'3/4': '75%',
|
||||||
|
full: '100%',
|
||||||
|
'-1/2': '-50%',
|
||||||
|
'-1/3': '-33.333333%',
|
||||||
|
'-2/3': '-66.666667%',
|
||||||
|
'-1/4': '-25%',
|
||||||
|
'-2/4': '-50%',
|
||||||
|
'-3/4': '-75%',
|
||||||
|
'-full': '-100%',
|
||||||
|
}),
|
||||||
|
width: (theme) => ({
|
||||||
|
auto: 'auto',
|
||||||
|
...theme('spacing'),
|
||||||
|
'1/2': '50%',
|
||||||
|
'1/3': '33.333333%',
|
||||||
|
'2/3': '66.666667%',
|
||||||
|
'1/4': '25%',
|
||||||
|
'2/4': '50%',
|
||||||
|
'3/4': '75%',
|
||||||
|
'1/5': '20%',
|
||||||
|
'2/5': '40%',
|
||||||
|
'3/5': '60%',
|
||||||
|
'4/5': '80%',
|
||||||
|
'1/6': '16.666667%',
|
||||||
|
'2/6': '33.333333%',
|
||||||
|
'3/6': '50%',
|
||||||
|
'4/6': '66.666667%',
|
||||||
|
'5/6': '83.333333%',
|
||||||
|
'1/12': '8.333333%',
|
||||||
|
'2/12': '16.666667%',
|
||||||
|
'3/12': '25%',
|
||||||
|
'4/12': '33.333333%',
|
||||||
|
'5/12': '41.666667%',
|
||||||
|
'6/12': '50%',
|
||||||
|
'7/12': '58.333333%',
|
||||||
|
'8/12': '66.666667%',
|
||||||
|
'9/12': '75%',
|
||||||
|
'10/12': '83.333333%',
|
||||||
|
'11/12': '91.666667%',
|
||||||
|
full: '100%',
|
||||||
|
screen: '100vw',
|
||||||
|
min: 'min-content',
|
||||||
|
max: 'max-content',
|
||||||
|
}),
|
||||||
|
zIndex: {
|
||||||
|
auto: 'auto',
|
||||||
|
0: '0',
|
||||||
|
10: '10',
|
||||||
|
20: '20',
|
||||||
|
30: '30',
|
||||||
|
40: '40',
|
||||||
|
50: '50',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
variantOrder: [
|
||||||
|
'first',
|
||||||
|
'last',
|
||||||
|
'odd',
|
||||||
|
'even',
|
||||||
|
'visited',
|
||||||
|
'checked',
|
||||||
|
'group-hover',
|
||||||
|
'group-focus',
|
||||||
|
'focus-within',
|
||||||
|
'hover',
|
||||||
|
'focus',
|
||||||
|
'focus-visible',
|
||||||
|
'active',
|
||||||
|
'disabled',
|
||||||
|
],
|
||||||
|
variants: {
|
||||||
|
accessibility: ['responsive', 'focus-within', 'focus'],
|
||||||
|
alignContent: ['responsive'],
|
||||||
|
alignItems: ['responsive'],
|
||||||
|
alignSelf: ['responsive'],
|
||||||
|
animation: ['responsive'],
|
||||||
|
appearance: ['responsive'],
|
||||||
|
backgroundAttachment: ['responsive'],
|
||||||
|
backgroundClip: ['responsive'],
|
||||||
|
backgroundColor: [
|
||||||
|
'responsive',
|
||||||
|
'dark',
|
||||||
|
'group-hover',
|
||||||
|
'focus-within',
|
||||||
|
'hover',
|
||||||
|
'focus',
|
||||||
|
],
|
||||||
|
backgroundImage: ['responsive'],
|
||||||
|
backgroundOpacity: [
|
||||||
|
'responsive',
|
||||||
|
'group-hover',
|
||||||
|
'focus-within',
|
||||||
|
'hover',
|
||||||
|
'focus',
|
||||||
|
],
|
||||||
|
backgroundPosition: ['responsive'],
|
||||||
|
backgroundRepeat: ['responsive'],
|
||||||
|
backgroundSize: ['responsive'],
|
||||||
|
borderCollapse: ['responsive'],
|
||||||
|
borderColor: [
|
||||||
|
'responsive',
|
||||||
|
'dark',
|
||||||
|
'group-hover',
|
||||||
|
'focus-within',
|
||||||
|
'hover',
|
||||||
|
'focus',
|
||||||
|
],
|
||||||
|
borderOpacity: [
|
||||||
|
'responsive',
|
||||||
|
'group-hover',
|
||||||
|
'focus-within',
|
||||||
|
'hover',
|
||||||
|
'focus',
|
||||||
|
],
|
||||||
|
borderRadius: ['responsive'],
|
||||||
|
borderStyle: ['responsive'],
|
||||||
|
borderWidth: ['responsive'],
|
||||||
|
boxShadow: ['responsive', 'group-hover', 'focus-within', 'hover', 'focus'],
|
||||||
|
boxSizing: ['responsive'],
|
||||||
|
clear: ['responsive'],
|
||||||
|
container: ['responsive'],
|
||||||
|
cursor: ['responsive'],
|
||||||
|
display: ['responsive'],
|
||||||
|
divideColor: ['responsive', 'dark'],
|
||||||
|
divideOpacity: ['responsive'],
|
||||||
|
divideStyle: ['responsive'],
|
||||||
|
divideWidth: ['responsive'],
|
||||||
|
fill: ['responsive'],
|
||||||
|
flex: ['responsive'],
|
||||||
|
flexDirection: ['responsive'],
|
||||||
|
flexGrow: ['responsive'],
|
||||||
|
flexShrink: ['responsive'],
|
||||||
|
flexWrap: ['responsive'],
|
||||||
|
float: ['responsive'],
|
||||||
|
fontFamily: ['responsive'],
|
||||||
|
fontSize: ['responsive'],
|
||||||
|
fontSmoothing: ['responsive'],
|
||||||
|
fontStyle: ['responsive'],
|
||||||
|
fontVariantNumeric: ['responsive'],
|
||||||
|
fontWeight: ['responsive'],
|
||||||
|
gap: ['responsive'],
|
||||||
|
gradientColorStops: ['responsive', 'dark', 'hover', 'focus'],
|
||||||
|
gridAutoColumns: ['responsive'],
|
||||||
|
gridAutoFlow: ['responsive'],
|
||||||
|
gridAutoRows: ['responsive'],
|
||||||
|
gridColumn: ['responsive'],
|
||||||
|
gridColumnEnd: ['responsive'],
|
||||||
|
gridColumnStart: ['responsive'],
|
||||||
|
gridRow: ['responsive'],
|
||||||
|
gridRowEnd: ['responsive'],
|
||||||
|
gridRowStart: ['responsive'],
|
||||||
|
gridTemplateColumns: ['responsive'],
|
||||||
|
gridTemplateRows: ['responsive'],
|
||||||
|
height: ['responsive'],
|
||||||
|
inset: ['responsive'],
|
||||||
|
justifyContent: ['responsive'],
|
||||||
|
justifyItems: ['responsive'],
|
||||||
|
justifySelf: ['responsive'],
|
||||||
|
letterSpacing: ['responsive'],
|
||||||
|
lineHeight: ['responsive'],
|
||||||
|
listStylePosition: ['responsive'],
|
||||||
|
listStyleType: ['responsive'],
|
||||||
|
margin: ['responsive'],
|
||||||
|
maxHeight: ['responsive'],
|
||||||
|
maxWidth: ['responsive'],
|
||||||
|
minHeight: ['responsive'],
|
||||||
|
minWidth: ['responsive'],
|
||||||
|
objectFit: ['responsive'],
|
||||||
|
objectPosition: ['responsive'],
|
||||||
|
opacity: ['responsive', 'group-hover', 'focus-within', 'hover', 'focus'],
|
||||||
|
order: ['responsive'],
|
||||||
|
outline: ['responsive', 'focus-within', 'focus'],
|
||||||
|
overflow: ['responsive'],
|
||||||
|
overscrollBehavior: ['responsive'],
|
||||||
|
padding: ['responsive'],
|
||||||
|
placeContent: ['responsive'],
|
||||||
|
placeItems: ['responsive'],
|
||||||
|
placeSelf: ['responsive'],
|
||||||
|
placeholderColor: ['responsive', 'dark', 'focus'],
|
||||||
|
placeholderOpacity: ['responsive', 'focus'],
|
||||||
|
pointerEvents: ['responsive'],
|
||||||
|
position: ['responsive'],
|
||||||
|
resize: ['responsive'],
|
||||||
|
ringColor: ['responsive', 'dark', 'focus-within', 'focus'],
|
||||||
|
ringOffsetColor: ['responsive', 'dark', 'focus-within', 'focus'],
|
||||||
|
ringOffsetWidth: ['responsive', 'focus-within', 'focus'],
|
||||||
|
ringOpacity: ['responsive', 'focus-within', 'focus'],
|
||||||
|
ringWidth: ['responsive', 'focus-within', 'focus'],
|
||||||
|
rotate: ['responsive', 'hover', 'focus'],
|
||||||
|
scale: ['responsive', 'hover', 'focus'],
|
||||||
|
skew: ['responsive', 'hover', 'focus'],
|
||||||
|
space: ['responsive'],
|
||||||
|
stroke: ['responsive'],
|
||||||
|
strokeWidth: ['responsive'],
|
||||||
|
tableLayout: ['responsive'],
|
||||||
|
textAlign: ['responsive'],
|
||||||
|
textColor: [
|
||||||
|
'responsive',
|
||||||
|
'dark',
|
||||||
|
'group-hover',
|
||||||
|
'focus-within',
|
||||||
|
'hover',
|
||||||
|
'focus',
|
||||||
|
],
|
||||||
|
textDecoration: [
|
||||||
|
'responsive',
|
||||||
|
'group-hover',
|
||||||
|
'focus-within',
|
||||||
|
'hover',
|
||||||
|
'focus',
|
||||||
|
],
|
||||||
|
textOpacity: [
|
||||||
|
'responsive',
|
||||||
|
'group-hover',
|
||||||
|
'focus-within',
|
||||||
|
'hover',
|
||||||
|
'focus',
|
||||||
|
],
|
||||||
|
textOverflow: ['responsive'],
|
||||||
|
textTransform: ['responsive'],
|
||||||
|
transform: ['responsive'],
|
||||||
|
transformOrigin: ['responsive'],
|
||||||
|
transitionDelay: ['responsive'],
|
||||||
|
transitionDuration: ['responsive'],
|
||||||
|
transitionProperty: ['responsive'],
|
||||||
|
transitionTimingFunction: ['responsive'],
|
||||||
|
translate: ['responsive', 'hover', 'focus'],
|
||||||
|
userSelect: ['responsive'],
|
||||||
|
verticalAlign: ['responsive'],
|
||||||
|
visibility: ['responsive'],
|
||||||
|
whitespace: ['responsive'],
|
||||||
|
width: ['responsive'],
|
||||||
|
wordBreak: ['responsive'],
|
||||||
|
zIndex: ['responsive', 'focus-within', 'focus'],
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user