added TailwindCSS and got it working as it was before
This commit is contained in:
parent
9979483be8
commit
b023279be5
47
index.html
47
index.html
@ -1,13 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta name="googlebot" content="index,follow">
|
||||
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta http-equiv="x-dns-prefetch-control" content="off">
|
||||
|
||||
<meta name="google" content="notranslate">
|
||||
<meta name="google" content="nositelinkssearchbox">
|
||||
|
||||
<meta name="rating" content="General">
|
||||
|
||||
|
||||
<meta name="subject" content="CSS color conversion tool">
|
||||
<meta name="description" content="Convert colors in a batch among HSL(a), RGB(a), and Hex">
|
||||
|
||||
<title>Batch Color Converter</title>
|
||||
|
||||
<!-- Privacy -->
|
||||
<meta name="twitter:dnt" content="on">
|
||||
|
||||
<!-- analytics -->
|
||||
<!-- none -->
|
||||
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</head>
|
||||
<body class="font-open-sans antialiased bg-zinc-50 dark:bg-slate-900">
|
||||
<div id="app" class="min-h-screen">
|
||||
<!-- -->
|
||||
</div>
|
||||
<!-- END OF LINE. -->
|
||||
</body>
|
||||
</html>
|
||||
|
69
index.php
Normal file
69
index.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
require_once "env.php";
|
||||
?><!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<meta name="robots" content="index,follow">
|
||||
<meta name="googlebot" content="index,follow">
|
||||
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta http-equiv="x-dns-prefetch-control" content="off">
|
||||
|
||||
<meta name="google" content="notranslate">
|
||||
<meta name="google" content="nositelinkssearchbox">
|
||||
|
||||
<meta name="rating" content="General">
|
||||
|
||||
<meta name="url" content="<?php echo $domain; ?>/">
|
||||
<meta name="subject" content="CSS color conversion tool">
|
||||
<meta name="description" content="Convert colors in a batch among HSL(a), RGB(a), and Hex">
|
||||
|
||||
<title>Batch Color Converter</title>
|
||||
|
||||
<!-- Privacy -->
|
||||
<meta name="twitter:dnt" content="on">
|
||||
|
||||
<!-- analytics -->
|
||||
<!-- none -->
|
||||
|
||||
<!-- Helps prevent duplicate content issues -->
|
||||
<link href="<?php echo $domain; ?>" rel="canonical">
|
||||
|
||||
<!-- Android web manifest file -->
|
||||
<!-- <link href="<?php echo $domain; ?>/.webmanifest" rel="manifest"> -->
|
||||
|
||||
<!-- Files listing who was involved in this site and copyrights -->
|
||||
<link href="<?php echo $domain; ?>/humans.txt" rel="author">
|
||||
<link href="<?php echo $domain; ?>/gpl-3.0-standalone.html" rel="copyright">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link href="<?php echo $domain; ?>/favicon.ico" rel="icon" sizes="16x16" type="image/icon">
|
||||
<link href="<?php echo $domain; ?>/favicon.svg" rel="icon" type="image/svg+xml">
|
||||
<link href="<?php echo $domain; ?>/favicon.png" rel="icon" sizes="192x192">
|
||||
|
||||
<!-- Font preloads (should be done for each font file) -->
|
||||
<!-- Open Sans license: fonts/OpenSans/LICENSE.txt -->
|
||||
<link href="<?php echo $domain; ?>/fonts/OpenSans/OpenSans-Regular.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<link href="<?php echo $domain; ?>/fonts/OpenSans/OpenSans-Bold.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<!-- Open Sans license: fonts/Ubuntu/UFL.txt -->
|
||||
<link href="<?php echo $domain; ?>/fonts/Ubuntu/Ubuntu-Regular.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
<link href="<?php echo $domain; ?>/fonts/Ubuntu/Ubuntu-Bold.woff2" rel="preload" as="font" type="font/woff2" crossorigin="anonymous">
|
||||
|
||||
<!-- CSS -->
|
||||
<link href="<?php echo $domain; ?>/css/vendor/tailwind-2.2.4.min.css" rel="stylesheet" media="screen">
|
||||
<link href="<?php echo $domain; ?>/css/app.css" rel="stylesheet" media="screen">
|
||||
|
||||
<!-- JS that must be executed before the document is loaded -->
|
||||
<script src="<?php echo $domain; ?>/js/vendor/vue-2.6.14.min.js" defer></script>
|
||||
<script src="<?php echo $domain; ?>/js/app.js" defer></script>
|
||||
<!-- -->
|
||||
</head>
|
||||
<body class="font-open-sans antialiased">
|
||||
|
||||
|
||||
<!-- END OF LINE. -->
|
||||
</body>
|
||||
</html>
|
1743
package-lock.json
generated
1743
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^2.2.2",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"postcss": "^8.4.12",
|
||||
"tailwindcss": "^3.0.23",
|
||||
"vite": "^2.8.4"
|
||||
}
|
||||
}
|
||||
|
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
326
src/App.vue
326
src/App.vue
@ -1,81 +1,261 @@
|
||||
<script setup>
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
import TheWelcome from './components/TheWelcome.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
|
||||
|
||||
<div class="grid-container h-screen">
|
||||
<div class="wrapper">
|
||||
<HelloWorld msg="You did it!" />
|
||||
<div class="grid-header">
|
||||
<h1 class="text-center text-4xl font-bold">Batch Color Converter</h1>
|
||||
<button @click="batchConvert" type="button">Convert</button>
|
||||
</div>
|
||||
<div class="grid-main">
|
||||
<div class="input-container">
|
||||
<textarea v-model="inputBox" class="resize-y w-full h-96 border border-gray-200 shadow rounded py-2 px-4" placeholder="#fff"></textarea>
|
||||
</div>
|
||||
<div class="output-wrapper">
|
||||
<table class="font-ubuntu w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="p-4">Preview</th>
|
||||
<th class="p-4">Hex(a)</th>
|
||||
<th class="p-4">RGB(a)</th>
|
||||
<th class="p-4">HSL(a)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(color, idx) in convertedColors" :key="idx" class="border-t border-gray-300">
|
||||
<td v-show="color.error" class="text-center px-2 py-4 bg-red-200 text-red-700 font-bold" colspan="4">Error with {{ color.raw }}</td>
|
||||
<td v-show="!color.error">
|
||||
<div class="block mx-2 my-4" style="height: 25px" :style="{ background: color.hex }"></div>
|
||||
</td>
|
||||
<td v-show="!color.error" class="color-cell cursor-pointer"><div class="text-center px-2 py-4 border border-transparent hover:border-gray-500" v-on:click="updateClipboard(color.hex)">{{ color.hex }}</div></td>
|
||||
<td v-show="!color.error" class="color-cell cursor-pointer"><div class="text-center px-2 py-4 border border-transparent hover:border-gray-500" v-on:click="updateClipboard(color.rgb)">{{ color.rgb }}</div></td>
|
||||
<td v-show="!color.error" class="color-cell cursor-pointer"><div class="text-center px-2 py-4 border border-transparent hover:border-gray-500" v-on:click="updateClipboard(color.hsl)">{{ color.hsl }}</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid-footer">
|
||||
<p>You can take a look at <a href="https://git.ditoforge.com/brian/batch-color-converter" title="Source code for this page" class="text-blue-400 font-bold hover:text-blue-700 transition">this terrible code over on my git server</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<TheWelcome />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, reactive, ref, computed, watch, onBeforeMount, onMounted } from 'vue'
|
||||
import { Color } from './converters/index.js'
|
||||
|
||||
export default defineComponent({
|
||||
emits: [],
|
||||
|
||||
props: {},
|
||||
|
||||
components: {},
|
||||
|
||||
setup(props, { attrs, slots, emit, expose }) {
|
||||
let defaultInputs = [
|
||||
"ccc",
|
||||
"#db2777aa",
|
||||
"rgb(64, 240, 18)",
|
||||
"hsla(240, 86.9%, 50.7%, 0.8)",
|
||||
]
|
||||
let inputBox = ref("ccc\n#4f46e5\n#db2777aa\n")
|
||||
|
||||
let convertedColors = ref([])
|
||||
|
||||
let showStatusMessage = ref(false)
|
||||
let showErrorMessage = ref(false)
|
||||
|
||||
//const hexRegex = /^\#?[a-fA-F0-9]{3,6}$/i
|
||||
//const hexRegex = /^(?=(\#?)[a-fA-F0-9]$)(?:.{3}|.{6})$/i
|
||||
const hexRegex = /^(\#?[a-fA-F0-9]{3}|\#?[a-fA-F0-9]{6})$/i
|
||||
//const hexaRegex = /^(\#?)[a-fA-F0-9]{4,8}$/i
|
||||
//const hexaRegex = /^(?=\#?[a-fA-F0-9]$)(?:.{4}|.{8})$/i
|
||||
const hexaRegex = /^(\#?[a-fA-F0-9]{4}|\#?[a-fA-F0-9]{8})$/i
|
||||
const rgbRegex = /^rgb\(\s*\d{1,3}\s*,?\s*\d{1,3}\s*,?\s*\d{1,3}\s*\)$/i
|
||||
//const rgbaRegex = /^rgba\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*(0(\.[0-9])?|1(\.0)?)\s*\)/i
|
||||
const rgbaRegex = /^rgb(a?)\(\s*\d{1,3}\s*,?\s*\d{1,3}\s*,?\s*\d{1,3}\s*(,|\/)*\s*((0(\.[0-9]{1,3}))?|(1(\.0{1,3}))?|([1-9][0-9]?|100))\s*\)$/i
|
||||
const hslRegex = /^hsl\(\s*\d{1,3}\s*,?\s*\d{1,3}(\.[0-9])?%\s*,?\s*\d{1,3}(\.[0-9])?%\s*\)\s*$/i
|
||||
//const hslaRegex = /^hsla\(\s*\d{1,3}\s*,*\s*\d{1,3}(\.[0-9])?%\s*,*\s*\d{1,3}(\.[0-9])?%\s*(,|\/)*\s*((0(\.[0-9]{1,3}))?|(1(\.0{1,3}))?)\s*\)/i
|
||||
const hslaRegex = /^hsl(a?)\(\s*\d{1,3}\s*,?\s*\d{1,3}(\.[0-9])?%\s*,?\s*\d{1,3}(\.[0-9])?%\s*(,|\/)*\s*((0(\.[0-9]{1,3}))?|(1(\.0{1,3}))?|([1-9][0-9]?|100))\s*\)\s*$/i
|
||||
|
||||
// computed properties
|
||||
/*let compVariable = computed(() => {
|
||||
//return 'foo'
|
||||
})*/
|
||||
|
||||
// watchers
|
||||
/*watch(inputBox, (newValue, oldValue) => {
|
||||
//
|
||||
})*/
|
||||
|
||||
// lifecycle hooks
|
||||
onBeforeMount(() => {
|
||||
inputBox.value = defaultInputs.join("\n")
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
batchConvert()
|
||||
})
|
||||
|
||||
// methods
|
||||
function batchConvert() {
|
||||
convertedColors.value = []
|
||||
|
||||
// first attempt to split by new-line characters
|
||||
let inputArray = inputBox.value.split('\n')
|
||||
let count = inputArray.length
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
// check for empty line, non hex(a)/rgb(a)/hsl(a) string
|
||||
let inputLine = inputArray[i].toLowerCase().trim()
|
||||
if (inputLine.length === 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
let converted = {
|
||||
error: false,
|
||||
raw: inputLine,
|
||||
hex: "",
|
||||
rgb: "",
|
||||
hsl: "",
|
||||
}
|
||||
|
||||
let rgb = { red: null, green: null, blue: null, alpha: null }
|
||||
let hsl = { hue: null, saturation: null, lightness: null, alpha: null }
|
||||
|
||||
if (hexRegex.test(inputLine)) {
|
||||
let hexString = inputLine
|
||||
|
||||
// doing this just to add pound symbol for the table output
|
||||
// if it doesn't already exist and to allow the use of
|
||||
// the hex value without leading pound symbol
|
||||
// for conversion to other formats.
|
||||
hexString = hexString.replace('#', '')
|
||||
|
||||
rgb = Color.hexToRgb(hexString)
|
||||
hsl = Color.rgbToHsl(rgb.red, rgb.green, rgb.blue)
|
||||
|
||||
converted.hex = "#" + hexString
|
||||
converted.rgb = "rgb(" + rgb.red + ", " + rgb.green + ", " + rgb.blue + ")"
|
||||
converted.hsl = "hsl(" + hsl.hue + ", " + hsl.saturation + "%, " + hsl.lightness + "%)"
|
||||
} else if (hexaRegex.test(inputLine)) {
|
||||
let hexString = inputLine
|
||||
hexString = hexString.replace('#', '')
|
||||
|
||||
rgb = Color.hexaToRgba(hexString)
|
||||
hsl = Color.rgbaToHsla(rgb.red, rgb.green, rgb.blue, rgb.alpha)
|
||||
|
||||
converted.hex = "#" + hexString
|
||||
converted.rgb = "rgba(" + rgb.red + ", " + rgb.green + ", " + rgb.blue + ", " + rgb.alpha + ")"
|
||||
converted.hsl = "hsla(" + hsl.hue + ", " + hsl.saturation + "%, " + hsl.lightness + "%, " + hsl.alpha + ")"
|
||||
}
|
||||
|
||||
if (rgbRegex.test(inputLine)) {
|
||||
// extract the values for red, green, blue.
|
||||
let rgbStrArray = inputLine.split(',')
|
||||
if (rgbStrArray.length !== 3) {
|
||||
rgbStrArray = inputLine.split(' ')
|
||||
}
|
||||
let red = Number.parseInt(rgbStrArray[0].replace('rgb(', '').trim())
|
||||
let green = Number.parseInt(rgbStrArray[1].trim())
|
||||
let blue = Number.parseInt(rgbStrArray[2].replace(')', '').trim())
|
||||
|
||||
hsl = Color.rgbToHsl(red, green, blue)
|
||||
|
||||
converted.hex = "#" + Color.rgbToHex(red, green, blue)
|
||||
converted.rgb = "rgb(" + red + ", " + green + ", " + blue + ")"
|
||||
converted.hsl = converted.hsl = "hsl(" + hsl.hue + ", " + hsl.saturation + "%, " + hsl.lightness + "%)"
|
||||
} else if (rgbaRegex.test(inputLine)) {
|
||||
// extract the values for red, green, blue, alpha.
|
||||
let alphaIndex = 3
|
||||
let rgbaStrArray = inputLine.split(',')
|
||||
if (rgbaStrArray.length !== 4) {
|
||||
alphaIndex = 4
|
||||
rgbaStrArray = inputLine.split(' ')
|
||||
}
|
||||
let red = Number.parseInt(rgbaStrArray[0].replace('rgb(', '').replace('rgba(', '').trim())
|
||||
let green = Number.parseInt(rgbaStrArray[1].trim())
|
||||
let blue = Number.parseInt(rgbaStrArray[2].trim())
|
||||
let alpha = rgbaStrArray[alphaIndex].replace(')', '').trim()
|
||||
if (alpha > 1) { alpha /= 100 }
|
||||
|
||||
hsl = Color.rgbToHsl(red, green, blue)
|
||||
|
||||
converted.hex = "#" + Color.rgbaToHexa(red, green, blue, alpha)
|
||||
converted.rgb = "rgba(" + red + ", " + green + ", " + blue + ", " + alpha + ")"
|
||||
converted.hsl = "hsla(" + hsl.hue + ", " + hsl.saturation + "%, " + hsl.lightness + "%, " + alpha + ")"
|
||||
}
|
||||
|
||||
if (hslRegex.test(inputLine)) {
|
||||
// extract the values for hue, saturation, lightness.
|
||||
let hslStrArray = inputLine.split(',')
|
||||
if (hslStrArray.length !== 3) {
|
||||
hslStrArray = inputLine.split(' ')
|
||||
}
|
||||
let hue = hslStrArray[0].replace('hsl(', '').trim()
|
||||
let saturation = hslStrArray[1].replace('%', '').trim()
|
||||
let lightness = hslStrArray[2].replace('%', '').replace(')', '').trim()
|
||||
|
||||
rgb = Color.hslToRgb(hue, saturation, lightness)
|
||||
|
||||
converted.hex = "#" + Color.hslToHex(hue, saturation, lightness)
|
||||
converted.rgb = "rgb(" + rgb.red + ", " + rgb.green + ", " + rgb.blue + ")"
|
||||
converted.hsl = "hsl(" + hue + ", " + saturation + "%, " + lightness + "%)"
|
||||
} else if (hslaRegex.test(inputLine)) {
|
||||
// extract the values for hue, saturation, lightness, alpha.
|
||||
let alphaIndex = 3
|
||||
let hslaStrArray = inputLine.split(',')
|
||||
if (hslaStrArray.length !== 4) {
|
||||
alphaIndex = 4
|
||||
hslaStrArray = inputLine.split(' ')
|
||||
}
|
||||
let hue = hslaStrArray[0].replace('hsl(', '').replace('hsla(', '').trim()
|
||||
let saturation = hslaStrArray[1].replace('%', '').trim()
|
||||
let lightness = hslaStrArray[2].replace('%', '').trim()
|
||||
let alpha = hslaStrArray[alphaIndex].replace(')', '').trim()
|
||||
if (alpha > 1) { alpha /= 100 }
|
||||
|
||||
rgb = Color.hslaToRgba(hue, saturation, lightness, alpha)
|
||||
|
||||
converted.hex = "#" + Color.hslaToHexa(hue, saturation, lightness, alpha)
|
||||
converted.rgb = "rgb(" + rgb.red + ", " + rgb.green + ", " + rgb.blue + ", " + alpha + ")"
|
||||
converted.hsl = "hsla(" + hue + ", " + saturation + "%, " + lightness + "%, " + alpha + ")"
|
||||
}
|
||||
|
||||
convertedColors.value.push(converted)
|
||||
}
|
||||
}
|
||||
|
||||
function updateClipboard (text) {
|
||||
clipboard.writeText(text).then(() => {
|
||||
showErrorMessage = false
|
||||
showStatusMessage = true
|
||||
setTimeout(() => {
|
||||
showStatusMessage = false
|
||||
}, 1000)
|
||||
})
|
||||
.catch(() => {
|
||||
showStatusMessage = false
|
||||
showErrorMessage = true
|
||||
setTimeout(() => {
|
||||
showErrorMessage = false
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
inputBox,
|
||||
convertedColors,
|
||||
showStatusMessage,
|
||||
showErrorMessage,
|
||||
batchConvert,
|
||||
updateClipboard,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@import './assets/base.css';
|
||||
@import './assets/base.css'
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
header {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
padding-right: calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
header .wrapper {
|
||||
display: flex;
|
||||
place-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 2rem 0 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
81
src/App.vue.old
Normal file
81
src/App.vue.old
Normal file
@ -0,0 +1,81 @@
|
||||
<script setup>
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
import TheWelcome from './components/TheWelcome.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
|
||||
|
||||
<div class="wrapper">
|
||||
<HelloWorld msg="You did it!" />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<TheWelcome />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@import './assets/base.css';
|
||||
|
||||
#app {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
header {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 2rem;
|
||||
}
|
||||
|
||||
a,
|
||||
.green {
|
||||
text-decoration: none;
|
||||
color: hsla(160, 100%, 37%, 1);
|
||||
transition: 0.4s;
|
||||
}
|
||||
|
||||
@media (hover: hover) {
|
||||
a:hover {
|
||||
background-color: hsla(160, 100%, 37%, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
body {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
#app {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
padding-right: calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
header .wrapper {
|
||||
display: flex;
|
||||
place-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 0 2rem 0 0;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,74 +1,55 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
.grid-container {
|
||||
@apply grid grid-flow-row grid-rows-1 grid-cols-1 gap-8;
|
||||
/*display: grid;*/
|
||||
/*grid-template-columns: 1fr;*/
|
||||
/*grid-template-rows: 1fr;*/
|
||||
/*gap: 2em 2em;*/
|
||||
/*grid-auto-flow: row;*/
|
||||
grid-template-areas:
|
||||
"wrapper";
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
.wrapper {
|
||||
@apply grid grid-flow-row grid-cols-1 gap-8;
|
||||
/*display: grid;*/
|
||||
/*grid-template-columns: 1fr;*/
|
||||
grid-template-rows: auto 1fr auto;
|
||||
/*gap: 2em 2em;*/
|
||||
/*grid-auto-flow: row;*/
|
||||
grid-template-areas:
|
||||
"grid-header"
|
||||
"grid-main"
|
||||
"grid-footer";
|
||||
grid-area: wrapper;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
.grid-header {
|
||||
@apply p-6;
|
||||
grid-area: grid-header;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
font-weight: normal;
|
||||
.grid-main {
|
||||
@apply grid grid-flow-col grid-rows-1 gap-8;
|
||||
/*display: grid;*/
|
||||
grid-template-columns: 30% 1fr;
|
||||
/*grid-template-rows: 1fr;*/
|
||||
/*gap: 2em 2em;*/
|
||||
/*grid-auto-flow: row;*/
|
||||
grid-template-areas:
|
||||
"input-container output-wrapper";
|
||||
grid-area: grid-main;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition: color 0.5s, background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
||||
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
.input-container {
|
||||
grid-area: input-container;
|
||||
}
|
||||
|
||||
.output-wrapper {
|
||||
grid-area: output-wrapper;
|
||||
}
|
||||
|
||||
.grid-footer {
|
||||
@apply p-6;
|
||||
grid-area: grid-footer;
|
||||
}
|
||||
|
||||
|
52
src/converters/hex.js
Normal file
52
src/converters/hex.js
Normal file
@ -0,0 +1,52 @@
|
||||
import { sanitizeAlphaFloat } from '../helpers'
|
||||
|
||||
const hexToRgb = (hexString) => {
|
||||
let hex = 'ffffff'
|
||||
if (hexString.length === 3) {
|
||||
hex = ''
|
||||
hex += hexString.substr(2, 1) + hexString.substr(2, 1)
|
||||
hex += hexString.substr(1, 1) + hexString.substr(1, 1)
|
||||
hex += hexString.substr(0, 1) + hexString.substr(0, 1)
|
||||
} else {
|
||||
hex = hexString
|
||||
}
|
||||
|
||||
let bigint = Number.parseInt(hex, 16)
|
||||
let redNum = (bigint >> 16) & 255
|
||||
let greenNum = (bigint >> 8) & 255
|
||||
let blueNum = bigint & 255
|
||||
|
||||
return { "red": redNum, "green": greenNum, "blue": blueNum }
|
||||
}
|
||||
|
||||
const hexaToRgba = (hexString) => {
|
||||
let hex = 'ffffff'
|
||||
let alphaHex = 'ff'
|
||||
let alpha = 1
|
||||
|
||||
if (hexString.length === 4) {
|
||||
alphaHex = hexString.substr(3, 1) + hexString.substr(3, 1)
|
||||
hex = ''
|
||||
hex += hexString.substr(2, 1) + hexString.substr(2, 1)
|
||||
hex += hexString.substr(1, 1) + hexString.substr(1, 1)
|
||||
hex += hexString.substr(0, 1) + hexString.substr(0, 1)
|
||||
} else {
|
||||
alphaHex = hexString.substr(6, 2)
|
||||
hex = hexString.substr(0, 6)
|
||||
}
|
||||
|
||||
let rgb = hexToRgb(hex)
|
||||
alpha = Number.parseFloat((Number.parseInt(alphaHex, 16) / 255)).toFixed(1)
|
||||
|
||||
return { "red": rgb.red, "green": rgb.green, "blue": rgb.blue, "alpha": alpha }
|
||||
}
|
||||
|
||||
const hexToHsl = (hexString) => {
|
||||
console.error('hex to HSL not implemented yet')
|
||||
}
|
||||
|
||||
const hexaToHsla = (hexString) => {
|
||||
console.error('hexa to HSLa not implemented yet')
|
||||
}
|
||||
|
||||
export { hexToRgb, hexaToRgba, hexToHsl, hexaToHsla }
|
81
src/converters/hsl.js
Normal file
81
src/converters/hsl.js
Normal file
@ -0,0 +1,81 @@
|
||||
import { sanitizeAlphaFloat } from '../helpers'
|
||||
|
||||
const hslToHex = (hue, saturation, lightness) => {
|
||||
let rgb = hslToRgb(hue, saturation, lightness)
|
||||
let red = rgb.red.toString(16)
|
||||
let green = rgb.green.toString(16)
|
||||
let blue = rgb.blue.toString(16)
|
||||
|
||||
if (red.length === 1) { red = "0" + red }
|
||||
if (green.length === 1) { green = "0" + green }
|
||||
if (blue.length === 1) { blue = "0" + blue }
|
||||
|
||||
return "" + red + green + blue
|
||||
}
|
||||
|
||||
const hslaToHexa = (hue, saturation, lightness, alpha) => {
|
||||
let rgb = hslToRgb(hue, saturation, lightness)
|
||||
let red = rgb.red.toString(16)
|
||||
let green = rgb.green.toString(16)
|
||||
let blue = rgb.blue.toString(16)
|
||||
let alphaHex = Math.round(sanitizeAlphaFloat(alpha) * 255).toString(16)
|
||||
|
||||
if (red.length === 1) { red = "0" + red }
|
||||
if (green.length === 1) { green = "0" + green }
|
||||
if (blue.length === 1) { blue = "0" + blue }
|
||||
|
||||
return "" + red + green + blue + alphaHex
|
||||
}
|
||||
|
||||
const hslToRgb = (hue, saturation, lightness) => {
|
||||
if (hue === 360) { hue = 0 }
|
||||
if (hue < 0) { hue += 360 }
|
||||
|
||||
saturation /= 100
|
||||
lightness /= 100
|
||||
|
||||
let chroma = (1 - Math.abs(2 * lightness - 1)) * saturation,
|
||||
xComponent = chroma * (1 - Math.abs((hue / 60) % 2 - 1)),
|
||||
lightnessMatch = lightness - (chroma / 2),
|
||||
red = 0,
|
||||
green = 0,
|
||||
blue = 0
|
||||
|
||||
// set red and green if hue between 0 and 119
|
||||
if (0 <= hue && hue < 60) {
|
||||
red = chroma
|
||||
green = xComponent
|
||||
} else if (60 <= hue && hue < 120) {
|
||||
red = xComponent
|
||||
green = chroma
|
||||
}
|
||||
// set green and blue if hue between 120 and 239
|
||||
else if (120 <= hue && hue < 180) {
|
||||
green = chroma
|
||||
blue = xComponent
|
||||
} else if (180 <= hue && hue < 240) {
|
||||
green = xComponent
|
||||
blue = chroma
|
||||
}
|
||||
// set red and blue if hue between 0 and 119
|
||||
else if (240 <= hue && hue < 300) {
|
||||
red = xComponent
|
||||
blue = chroma
|
||||
} else if (300 <= hue && hue < 360) {
|
||||
red = chroma
|
||||
blue = xComponent
|
||||
}
|
||||
|
||||
red = Math.round((red + lightnessMatch) * 255)
|
||||
green = Math.round((green + lightnessMatch) * 255)
|
||||
blue = Math.round((blue + lightnessMatch) * 255)
|
||||
|
||||
return { "red": red, "green": green, "blue": blue }
|
||||
}
|
||||
|
||||
const hslaToRgba = (hue, saturation, lightness, alpha) => {
|
||||
let rgb = hslToRgb(hue, saturation, lightness)
|
||||
return { "red": rgb.red, "green": rgb.green, "blue": rgb.blue, "alpha": sanitizeAlphaFloat(alpha) }
|
||||
}
|
||||
|
||||
export { hslToHex, hslaToHexa, hslToRgb, hslaToRgba }
|
11
src/converters/index.js
Normal file
11
src/converters/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { hexToRgb, hexaToRgba, hexToHsl, hexaToHsla } from './hex.js'
|
||||
import { hslToHex, hslaToHexa, hslToRgb, hslaToRgba } from './hsl.js'
|
||||
import { rgbToHex, rgbaToHexa, rgbToHsl, rgbaToHsla } from './rgb.js'
|
||||
|
||||
const Color = {
|
||||
hexToRgb, hexaToRgba, hexToHsl, hexaToHsla,
|
||||
hslToHex, hslaToHexa, hslToRgb, hslaToRgba,
|
||||
rgbToHex, rgbaToHexa, rgbToHsl, rgbaToHsla,
|
||||
}
|
||||
|
||||
export { Color }
|
66
src/converters/rgb.js
Normal file
66
src/converters/rgb.js
Normal file
@ -0,0 +1,66 @@
|
||||
import { sanitizeAlphaFloat } from '../helpers'
|
||||
|
||||
const rgbToHex = (redNum, greenNum, blueNum) => {
|
||||
let red = redNum.toString(16)
|
||||
let green = greenNum.toString(16)
|
||||
let blue = blueNum.toString(16)
|
||||
|
||||
if (red.length === 1) { red = "0" + red }
|
||||
if (green.length === 1) { green = "0" + green }
|
||||
if (blue.length === 1) { blue = "0" + blue }
|
||||
return "" + red + green + blue
|
||||
}
|
||||
|
||||
const rgbaToHexa = (redNum, greenNum, blueNum, alphaNum) => {
|
||||
let hex = rgbToHex(redNum, greenNum, blueNum)
|
||||
let alpha = Math.round(sanitizeAlphaFloat(alphaNum) * 255).toString(16)
|
||||
if (alpha.length == 1) { alpha = "0" + alpha }
|
||||
return hex + alpha
|
||||
}
|
||||
|
||||
const rgbToHsl = (redNum, greenNum, blueNum) => {
|
||||
let hue = 0, saturation = 0, lightness = 0
|
||||
|
||||
let redFrac = Number.parseFloat(redNum / 255),
|
||||
greenFrac = Number.parseFloat(greenNum / 255),
|
||||
blueFrac = Number.parseFloat(blueNum / 255)
|
||||
|
||||
let channelMin = Math.min(redFrac, blueFrac, greenFrac),
|
||||
channelMax = Math.max(redFrac, blueFrac, greenFrac),
|
||||
delta = channelMax - channelMin
|
||||
|
||||
if (delta === 0) { hue = 0 }
|
||||
// Red is the maximum color
|
||||
else if (channelMax === redFrac) {
|
||||
hue = ((greenFrac - blueFrac) / delta) % 6
|
||||
}
|
||||
// Green is the maximum color
|
||||
else if (channelMax === greenFrac) {
|
||||
hue = (blueFrac - redFrac) / delta + 2
|
||||
}
|
||||
// Green is the maximum color
|
||||
else {
|
||||
hue = (redFrac - greenFrac) / delta + 4
|
||||
}
|
||||
|
||||
hue = Math.round(hue * 60)
|
||||
// Make negative hues positive behind 360°
|
||||
if (hue < 0) { hue += 360 }
|
||||
|
||||
lightness = (channelMax + channelMin) / 2
|
||||
if (delta === 0) { saturation = 0 }
|
||||
else { saturation = delta / (1 - Math.abs(2 * lightness - 1)) }
|
||||
|
||||
lightness = +(lightness * 100).toFixed(1)
|
||||
saturation = +(saturation * 100).toFixed(1)
|
||||
|
||||
return { "hue": hue, "saturation": saturation, "lightness": lightness }
|
||||
}
|
||||
|
||||
const rgbaToHsla = (redNum, greenNum, blueNum, alphaNum) => {
|
||||
let hsl = rgbToHsl(redNum, greenNum, blueNum)
|
||||
hsl.alpha = sanitizeAlphaFloat(alphaNum)
|
||||
return hsl
|
||||
}
|
||||
|
||||
export { rgbToHex, rgbaToHexa, rgbToHsl, rgbaToHsla }
|
13
src/helpers/index.js
Normal file
13
src/helpers/index.js
Normal file
@ -0,0 +1,13 @@
|
||||
const sanitizeAlphaFloat = (alphaRaw) => {
|
||||
let alpha = Number.parseFloat(1)
|
||||
|
||||
if (alphaRaw >= 0 && alphaRaw <= 1) {
|
||||
alpha = Number.parseFloat(alphaRaw)
|
||||
} else if (alphaRaw < 0) {
|
||||
alpha = Number.parseFloat(0)
|
||||
}
|
||||
|
||||
return alpha.toFixed(1)
|
||||
}
|
||||
|
||||
export { sanitizeAlphaFloat }
|
43
src/index.css
Normal file
43
src/index.css
Normal file
@ -0,0 +1,43 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
src: url(../fonts/OpenSans/OpenSans-Regular.woff2) format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
src: url(../fonts/OpenSans/OpenSans-Bold.woff2) format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Ubuntu";
|
||||
src: url(../fonts/Ubuntu/Ubuntu-Regular.woff2) format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Ubuntu";
|
||||
src: url(../fonts/Ubuntu/Ubuntu-Bold.woff2) format("woff2");
|
||||
font-weight: 700;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
.font-open-sans {
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Open Sans", "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
|
||||
.font-ubuntu {
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Ubuntu", "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import './index.css'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
|
15
tailwind.config.js
Normal file
15
tailwind.config.js
Normal file
@ -0,0 +1,15 @@
|
||||
module.exports = {
|
||||
darkMode: "class",
|
||||
|
||||
content: [
|
||||
"./index.html",
|
||||
"./index.php",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
],
|
||||
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
|
||||
plugins: [],
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user