web: add basic styles
This commit is contained in:
parent
2f5a116dce
commit
af3a228edd
|
@ -8,6 +8,7 @@ export function Layout(page: AnyNode): AnyNode {
|
||||||
name: "viewport",
|
name: "viewport",
|
||||||
content: "width=device-width, initial-scale=1",
|
content: "width=device-width, initial-scale=1",
|
||||||
}),
|
}),
|
||||||
|
E("link", { rel: "stylesheet", href: "/static/styles.css" }),
|
||||||
E("title", [], "Recipes"),
|
E("title", [], "Recipes"),
|
||||||
]),
|
]),
|
||||||
E("body", [], [
|
E("body", [], [
|
||||||
|
|
|
@ -32,24 +32,30 @@ async function serveHttp(conn: Deno.Conn) {
|
||||||
const httpConn = Deno.serveHttp(conn);
|
const httpConn = Deno.serveHttp(conn);
|
||||||
|
|
||||||
for await (const reqEvt of httpConn) {
|
for await (const reqEvt of httpConn) {
|
||||||
const res = handleRequest(reqEvt.request);
|
const res = await handleRequest(reqEvt.request);
|
||||||
reqEvt.respondWith(res);
|
reqEvt.respondWith(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRequest(req: Request): Response {
|
async function handleRequest(req: Request): Promise<Response> {
|
||||||
log.debug({ url: req.url });
|
log.debug({ url: req.url });
|
||||||
const ren = new StrRenderer({ wrapNode: Layout });
|
|
||||||
const ctx = createContextFromRequest(req);
|
const ctx = createContextFromRequest(req);
|
||||||
|
|
||||||
if (ctx.locPath === "/") {
|
try {
|
||||||
return createHtmlResponse(ren.render(HomePage(ctx)));
|
const res = await tryCreateFileResponse(ctx.locPath);
|
||||||
} else if (ctx.locPath === "/recipes") {
|
return res;
|
||||||
return createHtmlResponse(ren.render(RecipesPage(ctx)));
|
} catch (_) {
|
||||||
} else if (ctx.locPath === "/ingredients") {
|
const ren = new StrRenderer({ wrapNode: Layout });
|
||||||
return createHtmlResponse(ren.render(IngredientsPage(ctx)));
|
|
||||||
} else {
|
if (ctx.locPath === "/") {
|
||||||
return createHtmlResponse(ren.render(E404Page(ctx)), 404);
|
return createHtmlResponse(ren.render(HomePage(ctx)));
|
||||||
|
} else if (ctx.locPath === "/recipes") {
|
||||||
|
return createHtmlResponse(ren.render(RecipesPage(ctx)));
|
||||||
|
} else if (ctx.locPath === "/ingredients") {
|
||||||
|
return createHtmlResponse(ren.render(IngredientsPage(ctx)));
|
||||||
|
} else {
|
||||||
|
return createHtmlResponse(ren.render(E404Page(ctx)), 404);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,6 +68,52 @@ function createContextFromRequest(req: Request): Context {
|
||||||
function createHtmlResponse(body: string, status = 200): Response {
|
function createHtmlResponse(body: string, status = 200): Response {
|
||||||
return new Response(body, {
|
return new Response(body, {
|
||||||
status,
|
status,
|
||||||
headers: { "content-type": "text/html" },
|
headers: getContentTypeHeader("html"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function tryCreateFileResponse(urlPath: string): Promise<Response> {
|
||||||
|
const filePath = extractFilePath(urlPath);
|
||||||
|
if (!filePath) throw new SkipFile();
|
||||||
|
|
||||||
|
const content = await Deno.readTextFile(filePath).catch(() => {
|
||||||
|
throw new SkipFile();
|
||||||
|
});
|
||||||
|
|
||||||
|
return createFileResponse(content, getFileExt(filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
class SkipFile extends Error {}
|
||||||
|
|
||||||
|
function createFileResponse(content: string, fileExt: string): Response {
|
||||||
|
log.debug(fileExt);
|
||||||
|
return new Response(content, {
|
||||||
|
headers: getContentTypeHeader(fileExt),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractFilePath(urlPath: string): string | null {
|
||||||
|
if (urlPath.startsWith("/static")) {
|
||||||
|
return urlPath.slice(1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContentTypeHeader(fileExt: string): Record<string, string> {
|
||||||
|
return { "content-type": getContentTypeByExt(fileExt) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContentTypeByExt(fileExt: string): string {
|
||||||
|
switch (fileExt) {
|
||||||
|
case "html":
|
||||||
|
return "text/html";
|
||||||
|
case "css":
|
||||||
|
return "text/css";
|
||||||
|
default:
|
||||||
|
return "text/plain";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileExt(filePath: string): string {
|
||||||
|
return filePath.slice((filePath.lastIndexOf(".") >>> 0) + 1);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
:root {
|
||||||
|
--min-width: 320px;
|
||||||
|
--max-width: 1024px;
|
||||||
|
|
||||||
|
--default-color-white: #ffffff;
|
||||||
|
--color-blue: #1966df;
|
||||||
|
|
||||||
|
--default-font-size: 16px;
|
||||||
|
--font-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Roboto,Oxygen-Sans, Ubuntu, Cantarell, "Segoe UI", Verdana, sans-serif;
|
||||||
|
--font-weight-regular: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *::before, *::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
background-color: var(--default-color-white);
|
||||||
|
font-size: var(--default-font-size);
|
||||||
|
line-height: 1;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-moz-text-size-adjust: 100%;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-weight: var(--font-weight-regular);
|
||||||
|
font-family: var(--font-family);
|
||||||
|
min-width: var(--min-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root {
|
||||||
|
min-height: 100vh;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#root,
|
||||||
|
#main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main,
|
||||||
|
.content {
|
||||||
|
flex: 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
padding-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
// anim
|
||||||
|
.main-menu > a {
|
||||||
|
transition: all .2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu > a {
|
||||||
|
color: var(--color-blue);
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--color-blue);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.main-menu > a:hover,
|
||||||
|
.main-menu > a[aria-current]:not([aria-current=""]) {
|
||||||
|
color: var(--default-color-white);
|
||||||
|
background-color: var(--color-blue);
|
||||||
|
}
|
||||||
|
.main-menu > a:not(:last-child) {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-width {
|
||||||
|
width: 100%;
|
||||||
|
max-width: var(--max-width);
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
padding-left: 1.25rem;
|
||||||
|
padding-right: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-typography h3 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-typography > div,
|
||||||
|
.responsive-typography p,
|
||||||
|
.responsive-typography li {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-typography ul,
|
||||||
|
.responsive-typography ol {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-typography ul li,
|
||||||
|
.responsive-typography ol li {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 1.5rem;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-typography ul li::before,
|
||||||
|
.responsive-typography ol li::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-typography ul > li::before {
|
||||||
|
background-color: var(--color-blue);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-typography > * + * {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-typography > div + div,
|
||||||
|
.responsive-typography p + p {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
.responsive-typography li + li {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
Loading…
Reference in New Issue