From 727d522e3a1aa2d2ecab14d5b9f828bebdb2fc17 Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Sun, 29 May 2022 22:55:01 +0300 Subject: [PATCH] web: use scss instead of css --- web/.gitignore | 13 +++ web/comp/layout.ts | 2 +- web/comp/page_layout.ts | 2 +- web/makefile | 7 +- web/server.ts | 8 +- web/static/styles.css | 245 --------------------------------------- web/views/e404.ts | 2 +- web/views/e500.ts | 2 +- web/views/home.ts | 2 +- web/views/ingredients.ts | 4 +- web/views/recipes.ts | 2 +- 11 files changed, 31 insertions(+), 258 deletions(-) create mode 100644 web/.gitignore delete mode 100644 web/static/styles.css diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 0000000..ef98814 --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,13 @@ +/* + +!/makefile +!/*ignore + +!/Dockerfile + +!/*json +!/*.ts + +!/(domain|repo|uikit|comp|views|translates)/*.ts +!/styles/*.scss + diff --git a/web/comp/layout.ts b/web/comp/layout.ts index d50e1c0..8b79236 100644 --- a/web/comp/layout.ts +++ b/web/comp/layout.ts @@ -9,7 +9,7 @@ export function Layout(ctx: Context, page: AnyNode): AnyNode { name: "viewport", content: "width=device-width, initial-scale=1", }), - E("link", { rel: "stylesheet", href: "/static/styles.css" }), + E("link", { rel: "stylesheet", href: "/styles/main.css" }), E("title", [], "Recipes"), ]), E("body", [], [ diff --git a/web/comp/page_layout.ts b/web/comp/page_layout.ts index 023950b..3e45f6a 100644 --- a/web/comp/page_layout.ts +++ b/web/comp/page_layout.ts @@ -32,7 +32,7 @@ function navLink(lhref: string, ctx?: Context): Attrs { export function Footer(ctx: Context): AnyNode { return E("footer", classNames("footer"), [ - E("div", classNames("content-width gap-v-1x5"), [ + E("div", classNames("content-width row-sta-bet"), [ Link(ctx.tr.Source_code, { target: "_blank", href: "https://notabug.org/pleshevskiy/recipes", diff --git a/web/makefile b/web/makefile index 1500a9e..90e4ed6 100644 --- a/web/makefile +++ b/web/makefile @@ -3,10 +3,13 @@ DOCKER_NAME := recipes DOCKER_TAG := recipes watch: + ${PAR} deno-w sass-w + +deno-w: deno run -A --watch server.ts -start: - deno run -A server.ts +sass-w: + sass -w styles/main.scss public/styles/main.css docker-restart: docker-stop docker-run diff --git a/web/server.ts b/web/server.ts index 0fae73e..084af8a 100644 --- a/web/server.ts +++ b/web/server.ts @@ -66,7 +66,7 @@ async function handleGet(req: Request) { const ren = new StrRenderer({ wrapNode: Layout.bind(null, ctx), onVisitAttr: ([key, value]) => { - if (key === "lhref") { + if (key === "lhref" && typeof value === "string") { return ["href", getLangHref(ctx.lang, value)]; } else { return [key, value]; @@ -137,6 +137,7 @@ function createHtmlResponse(body: string, status = 200): Response { async function tryCreateFileResponse(urlPath: string): Promise { const filePath = extractFilePath(urlPath); + log.debug({ filePath }); if (!filePath) throw new SkipFile(); const content = await Deno.readTextFile(filePath).catch(() => { @@ -155,8 +156,9 @@ function createFileResponse(content: string, fileExt: string): Response { } function extractFilePath(urlPath: string): string | null { - if (urlPath.startsWith("/static")) { - return urlPath.slice(1); + const relPath = urlPath.slice(1); + if (relPath.startsWith("styles/")) { + return `public/${relPath}`; } return null; } diff --git a/web/static/styles.css b/web/static/styles.css deleted file mode 100644 index 7da87e7..0000000 --- a/web/static/styles.css +++ /dev/null @@ -1,245 +0,0 @@ -:root { - --min-width: 320px; - --max-width: 1024px; - - --default-color-white: #ffffff; - --color-blue: #1966df; - --color-gray: #ccc; - - --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; -} - -.gap-v-1x5 { - padding-top: 1.5rem; - padding-bottom: 1.5rem; -} - -.footer { - border-top: solid 1px var(--color-gray); -} - -.footer > .content-width { - display: flex; - flex-direction: row; - justify-content: space-between; -} - -.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 */ - -.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; -} - -/* dropdown - * Source: https://codepen.io/markcaron/pen/wdVmpB - * - * TODO: change styles - * */ - -.dropdown { - position: relative; - display: inline-block; -} - -.dropdown > input[type="checkbox"] { - position: absolute; - left: -100vw; -} - -.dropdown > label { - display: inline-block; - padding: 6px 15px; - color: #333; - line-height: 1.5em; - text-decoration: none; - border: 1px solid #8c8c8c; - cursor: pointer; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.dropdown > label:hover { - border-color: #333; -} - -.dropdown > label:after { - content: "▲"; - font-size: 10px; - display: inline-block; - margin-left: 6px; - vertical-align: top; -} - -.dropdown > ul { - position: absolute; - z-index: 999; - display: block; - left: -100vw; - bottom: calc(1.5em + 14px); - border: 1px solid #8c8c8c; - background: #fff; - padding: 6px 0; - margin: 0; - list-style: none; - width: 100%; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - -webkit-box-shadow: 0 3px 8px rgba(0,0,0,.15); - -moz-box-shadow: 0 3px 8px rgba(0,0,0,.15); - box-shadow: 0 3px 8px rgba(0,0,0,.15); -} - -.dropdown > ul a { - display: block; - padding: 6px 15px; - text-decoration: none; - color: #333; -} - -.dropdown > ul a:hover, -.dropdown > ul a:focus { - background: #ececec; -} - -.dropdown > input[type="checkbox"]:checked ~ ul { - left: 0; -} - -.dropdown > [type="checkbox"]:checked + label:after { - content: "▼"; -} - diff --git a/web/views/e404.ts b/web/views/e404.ts index 03f9bdd..864fc75 100644 --- a/web/views/e404.ts +++ b/web/views/e404.ts @@ -9,7 +9,7 @@ export function E404Page(ctx: Context): AnyNode { } export function E404(ctx: Context): AnyNode { - return E("div", classNames("content-width"), [ + return E("div", classNames("content-width gap-v-1x5"), [ H3(ctx.tr.Page_not_found), ]); } diff --git a/web/views/e500.ts b/web/views/e500.ts index 7aeff05..dae2512 100644 --- a/web/views/e500.ts +++ b/web/views/e500.ts @@ -9,7 +9,7 @@ export function E500Page(ctx: Context): AnyNode { } export function E500(ctx: Context): AnyNode { - return E("div", classNames("content-width"), [ + return E("div", classNames("content-width gap-v-1x5"), [ H3(ctx.tr.Internal_server_error), ]); } diff --git a/web/views/home.ts b/web/views/home.ts index 94f71ce..6a429f6 100644 --- a/web/views/home.ts +++ b/web/views/home.ts @@ -6,7 +6,7 @@ import { H3 } from "../uikit/typo.ts"; export function HomePage(ctx: Context): AnyNode { return PageLayout(ctx, [ - E("div", classNames("content-width"), [ + E("div", classNames("content-width gap-v-1x5"), [ H3(ctx.tr.Home), ]), ]); diff --git a/web/views/ingredients.ts b/web/views/ingredients.ts index 3be5ed0..8f71070 100644 --- a/web/views/ingredients.ts +++ b/web/views/ingredients.ts @@ -14,7 +14,7 @@ export function IngredientsPage( data: IngredientsPageData, ): AnyNode { return PageLayout(ctx, [ - E("div", classNames("content-width"), [ + E("div", classNames("content-width gap-v-1x5"), [ H3(ctx.tr.Ingredients), IngredientList(data.ingredients), ]), @@ -23,7 +23,7 @@ export function IngredientsPage( export function IngredientList(ingrs: Ingredient[]): AnyNode { return E("div", classNames("responsive-typography"), [ - E("ul", classNames("gap-v-1x5"), ingrs.map(IngredientItem)), + E("ul", [], ingrs.map(IngredientItem)), ]); } diff --git a/web/views/recipes.ts b/web/views/recipes.ts index a243c45..cdbe173 100644 --- a/web/views/recipes.ts +++ b/web/views/recipes.ts @@ -6,7 +6,7 @@ import { H3 } from "../uikit/typo.ts"; export function RecipesPage(ctx: Context): AnyNode { return PageLayout(ctx, [ - E("div", classNames("content-width"), [ + E("div", classNames("content-width gap-v-1x5"), [ H3(ctx.tr.Recipes), ]), ]);