Initial design and configuration.

This commit is contained in:
Madeorsk 2024-06-08 23:52:26 +02:00
commit 47203d9082
28 changed files with 3512 additions and 0 deletions

10
.editorconfig Normal file
View file

@ -0,0 +1,10 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
[*.{js,jsx,ts,tsx,json,yml}]
charset = utf-8
indent_style = tab
indent_size = 2

4
.gitattributes vendored Normal file
View file

@ -0,0 +1,4 @@
/.yarn/** linguist-vendored
/.yarn/releases/* binary
/.yarn/plugins/**/* binary
/.pnp.* binary linguist-generated

21
.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
# IDEA
.idea/
*.iml
# YARN / NPM
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Swap the comments on the following lines if you wish to use zero-installs
# In that case, don't forget to run `yarn config set enableGlobalCache false`!
# Documentation here: https://yarnpkg.com/features/caching#zero-installs
#!.yarn/cache
node_modules/
.pnp.*

1
.yarnrc.yml Normal file
View file

@ -0,0 +1 @@
nodeLinker: node-modules

1
README.md Normal file
View file

@ -0,0 +1 @@
# Kernel UI Core

71
demo/DemoApp.tsx Normal file
View file

@ -0,0 +1,71 @@
import React from "react";
import "../index";
import {Checkbox} from "../src/Components/Forms/Checkbox";
import { Radio } from "../src/Components/Forms/Radio";
export function DemoApp()
{
return (
<div className={"app"}>
<h1>KernelUI</h1>
<h2>Headings</h2>
<h1>Demo app</h1>
<h2>Second title</h2>
<h3>Third title</h3>
<h4>Fourth title</h4>
<h5>Fifth title</h5>
<h6>Sixth title</h6>
<h2>Buttons</h2>
<button type={"button"}>A cool button</button>
<a className={"button"} href={"#"}>A link button</a>
<button type={"button"} className={"flat"}>A flat button</button>
<button type={"button"} className={"validation"}>A validation button</button>
<button type={"button"} className={"cancel"}>A cancellation button</button>
<button type={"button"} className={"delete"}>A deletion button</button>
<h2>Forms</h2>
<form>
<label>
Text label <span className={"required"}></span>
<input type={"text"} placeholder={"Normal demo text"}/>
</label>
<label>
Textarea label <span className={"required"}></span>
<textarea placeholder={"A normal textarea."}></textarea>
</label>
<Checkbox>Checkbox demo</Checkbox>
<Radio name={"radio-test"}>Radio box test</Radio>
<Radio name={"radio-test"}>Radio box test</Radio>
</form>
<h2>HTML</h2>
<div>
<a href={"#"}>Link test</a>
</div>
<p>
<strong>Lorem ipsum</strong> dolor <code>sit amet</code>, <em>consectetur</em> adipiscing elit. Donec accumsan pulvinar felis, vitae eleifend augue lacinia tempus. Integer nec iaculis ante. Duis a quam urna. Nullam tincidunt rutrum felis, a efficitur enim facilisis sit amet. Quisque dictum semper sagittis. Maecenas in orci hendrerit, tempor nunc non, tempus mi. Praesent blandit varius rutrum. Nullam quis mauris eros. Vestibulum commodo libero sed pellentesque pharetra. Donec eget fringilla ante. Aliquam id leo massa. Duis dictum nunc ut dolor iaculis malesuada. Nulla elementum justo a sem eleifend finibus. Phasellus bibendum elit nibh, at tempor odio efficitur id.
</p>
<h2>Steps</h2>
<div className={"steps"}>
<h3 className={"step"}>Step one</h3>
<h3 className={"step"}>Step two</h3>
<h3 className={"step"}>Step three</h3>
</div>
</div>
);
}

11
demo/demo.tsx Normal file
View file

@ -0,0 +1,11 @@
import React from "react";
import {createRoot} from "react-dom/client";
import {DemoApp} from "./DemoApp";
document.addEventListener("DOMContentLoaded", () => {
const demoApp = document.getElementById("demo-app");
const root = createRoot(demoApp);
root.render(<DemoApp />);
});

9
index.html Normal file
View file

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>UIKernel - Demo</title>
<script type="module" src="demo/demo.tsx"></script>
</head>
<body id="demo-app">
</body>
</html>

1
index.ts Normal file
View file

@ -0,0 +1 @@
import "./src/styles/main.less";

31
package.json Normal file
View file

@ -0,0 +1,31 @@
{
"name": "kernel-ui-core",
"description": "Kernel UI Core.",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "tsc && vite build"
},
"type": "module",
"source": "index.ts",
"types": "lib/index.d.ts",
"main": "lib/index.js",
"dependencies": {
"@fontsource-variable/jetbrains-mono": "^5.0.21",
"@fontsource-variable/manrope": "^5.0.20",
"@fontsource-variable/source-serif-4": "^5.0.19",
"@phosphor-icons/react": "^2.1.5",
"react": "^18.3.1"
},
"devDependencies": {
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.0",
"less": "^4.2.0",
"react-dom": "^18.3.1",
"typescript": "^5.4.5",
"vite": "^5.2.11",
"vite-plugin-dts": "^3.9.1"
},
"packageManager": "yarn@4.2.2"
}

View file

@ -0,0 +1,13 @@
import React from "react";
import {Check} from "@phosphor-icons/react";
export function Checkbox({children, type, ...inputProps}: React.PropsWithChildren<React.InputHTMLAttributes<HTMLInputElement>>): React.ReactElement
{
return (
<label className={"box"}>
<input type={"checkbox"} {...inputProps} />
<a className={"button"} tabIndex={-1}><Check weight={"bold"} /></a>
{children}
</label>
);
}

View file

@ -0,0 +1,13 @@
import React from "react";
import {Check} from "@phosphor-icons/react";
export function Radio({children, type, ...inputProps}: React.PropsWithChildren<React.InputHTMLAttributes<HTMLInputElement>>): React.ReactElement
{
return (
<label className={"box"}>
<input type={"radio"} {...inputProps} />
<a className={"button"} tabIndex={-1}><Check weight={"bold"} /></a>
{children}
</label>
);
}

62
src/styles/_colors.less Normal file
View file

@ -0,0 +1,62 @@
:root
{
@background-lightest: #FFFFFF; --background-lightest: @background-lightest;
@background-lighter: #F7F7F7; --background-lighter: @background-lighter;
@background: #EFEFEF; --background: @background;
@background-darker: #E7E7E7; --background-darker: @background-darker;
@background-darkest: #D9D9D9; --background-darkest: @background-darkest;
@neutral: #909090; --neutral: @neutral;
@foreground-lightest: #1F1F1F; --foreground-lightest: @foreground-lightest;
@foreground-lighter: #171717; --foreground-lighter: @foreground-lighter;
@foreground: #0F0F0F; --foreground: @foreground;
@foreground-darker: #080808; --foreground-darker: @foreground-darker;
@foreground-darkest: #000000; --foreground-darkest: @foreground-darkest;
@foreground-shadow: fade(@foreground, 50%); --foreground-shadow: @foreground-shadow;
@green-lighter: #22BD12; --green-lighter: @green-lighter;
@green: #1DA90F; --green: @green;
@green-darker: #159308; --green-darker: @green-darker;
@red-lighter: #E32424; --red-lighter: @red-lighter;
@red: #D01212; --red: @red;
@red-darker: #AF0707; --red-darker: @red-darker;
@blue-lighter: #378AFF; --blue-lighter: @blue-lighter;
@blue: #0D6DEE; --blue: @blue;
@blue-darker: #0657C5; --blue-darker: @blue-darker;
@orange-lighter: #E77220; --orange-lighter: @orange-lighter;
@orange: #D06112; --orange: @orange;
@orange-darker: #BB5308; --orange-darker: @orange-darker;
@pink-lighter: #EF56DF; --pink-lighter: @pink-lighter;
@pink: #CE3EBF; --pink: @pink;
@pink-darker: #B927AB; --pink-darker: @pink-darker;
@purple-lighter: #9752FF; --purple-lighter: @purple-lighter;
@purple: #7D2AFF; --purple: @purple;
@purple-darker: #6610EE; --purple-darker: @purple-darker;
@yellow-lighter: #F8DF3D; --yellow-lighter: @yellow-lighter;
@yellow: #EACD0D; --yellow: @yellow;
@yellow-darker: #D3B803; --yellow-darker: @yellow-darker;
@brown-lighter: #5E2617; --brown-lighter: @brown-lighter;
@brown: #4B190C; --brown: @brown;
@brown-darker: #3B1105; --brown-darker: @brown-darker;
@primary-lighter: @blue-lighter; --primary-lighter: @primary-lighter;
@primary: @blue; --primary: @primary;
@primary-darker: @blue-darker; --primary-darker: @primary-darker;
@secondary-lighter: @orange-lighter; --secondary-lighter: @secondary-lighter;
@secondary: @orange; --secondary: @secondary;
@secondary-darker: @orange-darker; --secondary-darker: @secondary-darker;
}

29
src/styles/_common.less Normal file
View file

@ -0,0 +1,29 @@
html, body
{
margin: 0;
padding: 0;
}
html, body, input, button, textarea
{
font-size: 1em;
font-family: @sans-serif-fonts;
font-weight: 500;
}
pre, code
{
font-family: @monospace-fonts;
}
body
{
background: var(--background);
color: var(--foreground);
}
p
{
margin: 0.75em auto;
line-height: 1.6em;
}

View file

@ -0,0 +1,6 @@
@import "components/_button";
@import "components/_form";
@import "components/_headings";
@import "components/_link";
@import "components/_steps";

7
src/styles/_fonts.less Normal file
View file

@ -0,0 +1,7 @@
@import "@fontsource-variable/manrope";
@import "@fontsource-variable/source-serif-4";
@import "@fontsource-variable/jetbrains-mono";
@sans-serif-fonts: "Manrope Variable", sans-serif;
@serif-fonts: "Source Serif 4 Variable", serif;
@monospace-fonts: "Jetbrains Mono Variable", monospace;

View file

@ -0,0 +1,104 @@
a.button, button, input[type="submit"], input[type="reset"]
{
transition: background 0.2s ease, outline 0.2s ease, transform 0.1s ease;
display: inline-block;
margin: auto 0.33em;
padding: 0.45em 0.66em;
width: fit-content;
border-radius: 0.25em;
box-sizing: border-box;
box-shadow: 0 0 0.3em 0 var(--foreground-shadow);
outline: solid 2px transparent;
outline-offset: 2px;
border: solid var(--primary-darker) thin;
background: var(--primary);
color: var(--background);
font-weight: 600;
text-decoration: none;
cursor: pointer;
transform: scale(1);
transform-origin: center;
&:hover
{
background: var(--primary-lighter);
}
&:focus
{
outline: solid 2px var(--primary);
outline-offset: 2px;
}
&:active
{
transform: scale(0.95);
}
&.flat
{
box-shadow: 0 0 0 0 transparent;
border-color: var(--background-darkest);
background: var(--background-lighter);
color: var(--foreground);
&:hover
{
background: var(--background-lightest);
}
&:focus
{
outline-color: var(--neutral);
}
}
&.green, &.validation, &.ok, &.positive, &.good
{
border-color: var(--green-darker);
background: var(--green);
&:hover
{
background: var(--green-lighter);
}
&:focus
{
outline-color: var(--green);
}
}
&.orange, &.cancel, &.back, &.return
{
border-color: var(--orange-darker);
background: var(--orange);
&:hover
{
background: var(--orange-lighter);
}
&:focus
{
outline-color: var(--orange);
}
}
&.red, &.delete, &.no, &.negative, &.bad
{
border-color: var(--red-darker);
background: var(--red);
&:hover
{
background: var(--red-lighter);
}
&:focus
{
outline-color: var(--red);
}
}
}

View file

@ -0,0 +1,4 @@
@import "forms/_box";
@import "forms/_input";
@import "forms/_label";

View file

@ -0,0 +1,40 @@
h1, h2, h3, h4, h5, h6
{
margin: 0.8em auto;
}
h1
{
font-size: 2.5em;
font-weight: 800;
}
h2
{
font-size: 2em;
font-weight: 800;
}
h3
{
font-size: 1.7em;
font-weight: 700;
}
h4
{
font-size: 1.5em;
font-weight: 600;
}
h5
{
font-size: 1.3em;
font-weight: 600;
}
h6
{
font-size: 1.15em;
font-weight: 600;
}

View file

@ -0,0 +1,4 @@
a
{
color: var(--primary);
}

View file

@ -0,0 +1,24 @@
.steps
{
counter-reset: steps-count 0;
.step
{
&::before
{
content: counter(steps-count);
display: inline-block;
margin: 0 0.2em;
color: var(--primary);
font-family: @monospace-fonts;
font-size: 1.5em;
font-weight: 700;
vertical-align: baseline;
}
counter-increment: steps-count;
}
}

View file

@ -0,0 +1,52 @@
label.box
{
display: block;
&::before
{
content: unset;
}
> input
{
position: absolute;
pointer-events: none;
opacity: 0;
}
> input[type="radio"] + .button
{
border-radius: 50%;
}
> .button
{
margin: 0 0.6em 0.12em 0;
width: 1.5em;
height: 1.5em;
padding: 0;
border: none;
color: var(--background-darkest);
> svg
{
display: block;
margin: 0.22em auto 0;
vertical-align: middle;
}
vertical-align: middle;
}
&:focus-within > .button
{
outline-color: var(--primary);
}
> input:not(:checked) + .button
{
background: var(--background-lighter);
}
}

View file

@ -0,0 +1,26 @@
input, textarea, select
{
transition: outline 0.2s ease;
display: block;
padding: 0.5em;
border-radius: 0.25em;
box-sizing: border-box;
border: solid var(--background-darkest) thin;
outline: solid 2px transparent;
outline-offset: 2px;
background: var(--background-lighter);
&:focus
{
outline: solid 2px var(--primary);
outline-offset: 2px;
}
&::placeholder
{
color: var(--neutral);
opacity: 1;
}
}

View file

@ -0,0 +1,14 @@
label
{
display: block;
margin: 0.75em auto;
width: fit-content;
&::before { content: ""; display: inline-block; width: 0.5em; }
input, textarea, select { margin-top: 0.25em; }
span.required
{
&::after { content: "*"; color: var(--red); font-weight: 600; }
}
}

7
src/styles/main.less Normal file
View file

@ -0,0 +1,7 @@
@import "_fonts";
@import "_colors";
@import "_common";
@import "_components";

28
tsconfig.json Normal file
View file

@ -0,0 +1,28 @@
{
"ts-node": {
"compilerOptions": {
"module": "ESNext",
"types": ["node"]
}
},
"compilerOptions": {
"outDir": "./lib",
"incremental": true,
"sourceMap": true,
"noImplicitAny": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "ES6",
"target": "ES6",
"moduleResolution": "Node",
"lib": [
"ESNext",
"DOM"
],
"jsx": "react",
"allowJs": true
}
}

29
vite.config.ts Normal file
View file

@ -0,0 +1,29 @@
import {ConfigEnv, defineConfig, UserConfig} from "vite";
import dts from "vite-plugin-dts";
import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
return ({
build: {
outDir: "lib",
sourcemap: true,
lib: {
entry: "index.ts",
formats: ["es"],
fileName: "index",
},
rollupOptions: {
external: ["react"],
},
},
plugins: [
react(),
dts({
insertTypesEntry: true,
}),
]
});
});

2890
yarn.lock Normal file

File diff suppressed because it is too large Load diff