feat: Add tailwind and cloudflare support

This commit is contained in:
Wanjohi
2024-08-30 10:22:10 +03:00
parent 81fc5e8f85
commit fd16947e14
21 changed files with 558 additions and 24 deletions

3
apps/www/.gitignore vendored
View File

@@ -39,3 +39,6 @@ lerna-debug.log*
# Yarn
.yarn/*
!.yarn/releases
# Cloudflare
functions/**/*.js

View File

@@ -63,3 +63,50 @@ The production build will generate client and server modules by running both cli
```shell
bun build # or `bun build`
```
## Cloudflare Pages
Cloudflare's [wrangler](https://github.com/cloudflare/wrangler) CLI can be used to preview a production build locally. To start a local server, run:
```
bun serve
```
Then visit [http://localhost:8787/](http://localhost:8787/)
### Deployments
[Cloudflare Pages](https://pages.cloudflare.com/) are deployable through their [Git provider integrations](https://developers.cloudflare.com/pages/platform/git-integration/).
If you don't already have an account, then [create a Cloudflare account here](https://dash.cloudflare.com/sign-up/pages). Next go to your dashboard and follow the [Cloudflare Pages deployment guide](https://developers.cloudflare.com/pages/framework-guides/deploy-anything/).
Within the projects "Settings" for "Build and deployments", the "Build command" should be `bun build`, and the "Build output directory" should be set to `dist`.
### Function Invocation Routes
Cloudflare Page's [function-invocation-routes config](https://developers.cloudflare.com/pages/platform/functions/routing/#functions-invocation-routes) can be used to include, or exclude, certain paths to be used by the worker functions. Having a `_routes.json` file gives developers more granular control over when your Function is invoked.
This is useful to determine if a page response should be Server-Side Rendered (SSR) or if the response should use a static-site generated (SSG) `index.html` file.
By default, the Cloudflare pages adaptor _does not_ include a `public/_routes.json` config, but rather it is auto-generated from the build by the Cloudflare adaptor. An example of an auto-generate `dist/_routes.json` would be:
```
{
"include": [
"/*"
],
"exclude": [
"/_headers",
"/_redirects",
"/build/*",
"/favicon.ico",
"/manifest.json",
"/service-worker.js",
"/about"
],
"version": 1
}
```
In the above example, it's saying _all_ pages should be SSR'd. However, the root static files such as `/favicon.ico` and any static assets in `/build/*` should be excluded from the Functions, and instead treated as a static file.
In most cases the generated `dist/_routes.json` file is ideal. However, if you need more granular control over each path, you can instead provide you're own `public/_routes.json` file. When the project provides its own `public/_routes.json` file, then the Cloudflare adaptor will not auto-generate the routes config and instead use the committed one within the `public` directory.

View File

@@ -0,0 +1,15 @@
import { cloudflarePagesAdapter } from "@builder.io/qwik-city/adapters/cloudflare-pages/vite";
import { extendConfig } from "@builder.io/qwik-city/vite";
import baseConfig from "../../vite.config";
export default extendConfig(baseConfig, () => {
return {
build: {
ssr: true,
rollupOptions: {
input: ["src/entry.cloudflare-pages.tsx", "@qwik-city-plan"],
},
},
plugins: [cloudflarePagesAdapter()],
};
});

View File

@@ -15,20 +15,25 @@
"build": "qwik build",
"build.client": "vite build",
"build.preview": "vite build --ssr src/entry.preview.tsx",
"build.server": "vite build -c adapters/cloudflare-pages/vite.config.ts",
"build.types": "tsc --incremental --noEmit",
"deploy": "echo 'Run \"npm run qwik add\" to install a server adapter'",
"deploy": "wrangler pages deploy ./dist",
"dev": "vite --mode ssr",
"dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
"fmt": "prettier --write .",
"fmt.check": "prettier --check .",
"lint": "eslint \"src/**/*.ts*\"",
"preview": "qwik build preview && vite preview --open",
"serve": "wrangler pages dev ./dist --compatibility-flags=nodejs_als",
"start": "vite --open --mode ssr",
"qwik": "qwik"
},
"devDependencies": {
"@builder.io/qwik": "^1.8.0",
"@builder.io/qwik-city": "^1.8.0",
"@nestri/eslint-config": "workspace:*",
"@nestri/typescript-config": "workspace:*",
"@nestri/ui": "workspace:*",
"@types/eslint": "8.56.10",
"@types/node": "20.14.11",
"@typescript-eslint/eslint-plugin": "7.16.1",
@@ -39,6 +44,7 @@
"typescript": "5.4.5",
"undici": "*",
"vite": "5.3.5",
"vite-tsconfig-paths": "^4.2.1"
"vite-tsconfig-paths": "^4.2.1",
"wrangler": "^3.0.0"
}
}

View File

@@ -0,0 +1 @@
module.exports = require("@nestri/ui/postcss");

9
apps/www/public/_headers Normal file
View File

@@ -0,0 +1,9 @@
# https://developers.cloudflare.com/pages/platform/headers/
/*service-worker.js
Cache-Control: no-store
Content-Type: application/javascript
X-Content-Type-Options: nosniff
/build/*
Cache-Control: public, max-age=31536000, s-maxage=31536000, immutable

View File

@@ -0,0 +1 @@
# https://developers.cloudflare.com/pages/platform/redirects/

View File

@@ -0,0 +1,24 @@
/*
* WHAT IS THIS FILE?
*
* It's the entry point for Cloudflare Pages when building for production.
*
* Learn more about the Cloudflare Pages integration here:
* - https://qwik.dev/docs/deployments/cloudflare-pages/
*
*/
import {
createQwikCity,
type PlatformCloudflarePages,
} from "@builder.io/qwik-city/middleware/cloudflare-pages";
import qwikCityPlan from "@qwik-city-plan";
import { manifest } from "@qwik-client-manifest";
import render from "./entry.ssr";
declare global {
interface QwikCityPlatform extends PlatformCloudflarePages {}
}
const fetch = createQwikCity({ render, qwikCityPlan, manifest });
export { fetch };

View File

@@ -7,7 +7,7 @@ import {
import { RouterHead } from "./components/router-head/router-head";
import { isDev } from "@builder.io/qwik/build";
import "./global.css";
import "@nestri/ui/globals.css";
export default component$(() => {
/**

View File

@@ -3,14 +3,9 @@ import type { DocumentHead } from "@builder.io/qwik-city";
export default component$(() => {
return (
<>
<h1>Hi 👋</h1>
<div>
Can't wait to see what you build with qwik!
<br />
Happy coding.
</div>
</>
<div class="bg-primary-500 h-screen w-screen">
</div>
);
});

View File

@@ -0,0 +1,11 @@
// import colors from "tailwindcss/colors";
import baseConfig from "@nestri/ui/tailwind.config";
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./{src,components,app}/**/*.{ts,tsx,html}",
"../../packages/ui/src/**/*.{ts,tsx}",
],
presets: [baseConfig],
plugins: [],
};

View File

@@ -21,5 +21,5 @@
}
},
"files": ["./.eslintrc.cjs"],
"include": ["src", "./*.d.ts", "./*.config.ts"]
"include": ["src", "./*.d.ts", "./*.config.ts", "./*.config.js", "postcss.config.cjs"]
}

BIN
bun.lockb

Binary file not shown.

View File

@@ -5,15 +5,21 @@
"files": [
"library.js",
"next.js",
"react-internal.js"
"react-internal.js",
"qwik.js"
],
"devDependencies": {
"@vercel/style-guide": "^5.2.0",
"eslint-config-turbo": "^2.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-only-warn": "^1.1.0",
"@typescript-eslint/parser": "^7.1.0",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"typescript": "^5.3.3"
"@types/eslint": "8.56.10",
"@types/node": "20.14.11",
"@typescript-eslint/eslint-plugin": "7.16.1",
"@typescript-eslint/parser": "7.16.1",
"eslint": "8.57.0",
"eslint-plugin-qwik": "^1.8.0",
"prettier": "3.3.3",
"typescript": "5.4.5"
}
}

View File

@@ -0,0 +1,42 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:qwik/recommended",
],
parser: "@typescript-eslint/parser",
parserOptions: {
tsconfigRootDir: __dirname,
project: ["./tsconfig.json"],
ecmaVersion: 2021,
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
plugins: ["@typescript-eslint"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/ban-ts-comment": "off",
"prefer-spread": "off",
"no-case-declarations": "off",
"no-console": "off",
"@typescript-eslint/no-unused-vars": ["error"],
"@typescript-eslint/consistent-type-imports": "warn",
"@typescript-eslint/no-unnecessary-condition": "warn",
},
};

223
packages/ui/globals.css Normal file
View File

@@ -0,0 +1,223 @@
@tailwind components;
@tailwind base;
@tailwind utilities;
@layer base {
html,
html * {
scrollbar-color: theme("colors.gray.700") theme("colors.gray.300");
scrollbar-width: thin;
}
*::selection {
background-color: theme("colors.primary.200");
}
*::-moz-selection {
background-color: theme("colors.primary.200");
}
html.dark *::selection {
background-color: theme("colors.primary.800");
}
html.dark *::-moz-selection {
background-color: theme("colors.primary.800");
}
html.dark,
html.dark * {
scrollbar-color: theme("colors.gray.300") theme("colors.gray.700");
scrollbar-width: thin;
}
@media (prefers-color-scheme: dark) {
*::selection {
background-color: theme("colors.primary.800");
}
*::-moz-selection {
background-color: theme("colors.primary.800");
}
html,
html * {
scrollbar-color: theme("colors.gray.300") theme("colors.gray.700");
scrollbar-width: thin;
}
}
input:-webkit-autofill,
input:-webkit-autofill:focus {
transition: background-color 0s 600000s, color 0s 600000s !important;
}
}
.marquee-container {
width: 100%;
padding: 1rem;
scale: var(--scale);
}
.marquee-container[data-spill=true]::after {
--padding-x: 1rem;
--padding-y: 1rem;
content: "";
position: fixed;
top: 50%;
left: 50%;
background: hsl(10 80% 0% / 0.25);
width: calc(var(--scale) * 10000vw);
height: calc(var(--scale) * 10000vh);
pointer-events: none;
translate: -50% -50%;
mask:
linear-gradient(white, white) 50% 50% / 100% 100% no-repeat,
linear-gradient(white, white) 50% 50% / calc(100cqi + (var(--padding-x) * 2)) calc(100cqh + (var(--padding-y) * 2)) no-repeat;
mask-composite: exclude;
}
.marquee-container:not([data-spill=true]) {
overflow: hidden;
}
[data-direction=horizontal] {
height: 100%;
}
[data-direction=vertical] {
width: 100%;
}
.marquee-container ul {
display: flex;
padding: 0;
margin: 0;
list-style-type: none;
}
[data-reverse=true] * {
animation-direction: reverse !important;
}
[data-translate=track][data-direction=horizontal] ul {
--destination-x: -100%;
animation: track-translate calc(var(--speed) * 1s) infinite linear;
}
[data-translate=track][data-direction=vertical] ul {
--destination-y: -100%;
animation: track-translate calc(var(--speed) * 1s) infinite linear;
}
[data-translate=track][data-direction=horizontal][data-pad=true] ul {
--destination-x: calc((100% / -3) * 2);
translate: calc(100% / -3) 0;
}
[data-translate=track][data-direction=vertical][data-pad=true] ul {
--destination-y: calc((100% / -3) * 2);
translate: 0 calc(100% / -3);
}
[data-pad-diff=true] .pad {
background: hsl(0 0% 10%);
color: hsl(0 0% 98%);
}
@keyframes track-translate {
to {
translate: var(--destination-x, 0) var(--destination-y, 0);
}
}
[data-direction=horizontal] ul {
height: max-content;
width: fit-content;
align-items: center;
}
[data-direction=vertical] ul {
width: 100%;
height: fit-content;
justify-items: center;
flex-direction: column;
}
/* .marquee-container ul li {
height: auto;
aspect-ratio: 4 / 3;
background: hsl(0 0% 90%);
border-radius: 6px;
font-size: clamp(2rem, 4vw + 1rem, 8rem);
display: grid;
place-items: center;
border: 1px solid hsl(0 0% 50%);
} */
[data-play-state=running] :is(ul, li) {
animation-play-state: running !important;
}
[data-play-state=paused] :is(ul, li) {
animation-play-state: paused !important;
}
/* The animation stuff */
@media(prefers-reduced-motion: no-preference) {
[data-translate=items] ul {
gap: 0;
}
[data-translate=items][data-direction=horizontal].marquee-container {
padding-inline: 0;
}
[data-translate=items][data-direction=vertical].marquee-container {
padding-block: 0;
}
[data-translate=items][data-spill=true][data-direction=horizontal].marquee-container::after {
--padding-x: 0rem;
}
[data-translate=items][data-direction=vertical][data-spill=true].marquee-container::after {
--padding-y: 0rem;
}
[data-pause-on-hover=true]:hover li {
animation-play-state: paused !important;
}
[data-translate=items] li {
--duration: calc(var(--speed) * 1s);
--delay: calc((var(--duration) / var(--count)) * (var(--index, 0) - (var(--count) * 0.5)));
animation: slide var(--duration) calc(var(--delay) - (var(--count) * 0.5s)) infinite linear paused;
translate: var(--origin-x) var(--origin-y);
}
[data-translate=items][data-direction=horizontal] li {
--origin-x: calc(((var(--count) - var(--index)) + var(--inset, 0)) * 100%);
--origin-y: 0;
--destination-x: calc(calc((var(--index) + 1 + var(--outset, 0)) * -100%));
--destination-y: 0;
}
[data-translate=items][data-direction=vertical] li {
--origin-x: 0;
--origin-y: calc(((var(--count) - var(--index)) + var(--inset, 0)) * 100%);
--destination-x: 0;
--destination-y: calc(calc((var(--index) + 1 + var(--outset, 0)) * -100%));
}
@keyframes slide {
100% {
translate: var(--destination-x) var(--destination-y);
}
}
}

View File

@@ -5,24 +5,35 @@
"exports": {
"./button": "./src/button.tsx",
"./card": "./src/card.tsx",
"./code": "./src/code.tsx"
"./code": "./src/code.tsx",
"./tailwind.config": "./tailwind.config.js",
"./globals.css": "./globals.css",
"./postcss": "./post.config.js"
},
"files": ["tailwind.config.js", "post.config.js", "globals.css", "postcss.config.js"],
"scripts": {
"lint": "eslint . --max-warnings 0",
"generate:component": "turbo gen react-component"
},
"devDependencies": {
"@builder.io/qwik": "^1.8.0",
"@builder.io/qwik-react": "^0.5.5",
"@nestri/eslint-config": "*",
"@nestri/typescript-config": "*",
"@turbo/gen": "^1.12.4",
"@types/node": "^20.11.24",
"@types/eslint": "^8.56.5",
"@types/react": "^18.2.61",
"@types/react-dom": "^18.2.19",
"@types/node": "^20.11.24",
"@types/react": "^18.3.4",
"@types/react-dom": "^18.3.0",
"autoprefixer": "^10.4.20",
"eslint": "^8.57.0",
"framer-motion": "^11.3.31",
"postcss": "^8.4.41",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"tailwind-merge": "^2.5.2",
"tailwind-variants": "^0.2.1",
"tailwindcss": "^3.4.10",
"typescript": "^5.3.3"
},
"dependencies": {
"react": "^18.2.0"
}
}

View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View File

@@ -0,0 +1,134 @@
import colors from "tailwindcss/colors";
/** @type {import('tailwindcss').Config} */
export default {
content: [
'./{src,components,app}/**/*.{ts,tsx,html}',
],
theme: {
extend: {
colors: {
primary: {
50: "#FFEDE5",
100: "#FFDBCC",
200: "#FFB899",
300: "#FF9466",
400: "#FF7033",
500: "#FF4F01",
600: "#CC3D00",
700: "#992E00",
800: "#661F00",
900: "#330F00",
950: "#190800",
DEFAULT: "#FF4F01"
},
secondary: colors.orange,
accent: colors.amber,
gray: {
...colors.neutral,
925: "#111111",
},
danger: colors.red,
warning: colors.yellow,
success: colors.green,
info: colors.blue,
},
fontFamily: {
body: [
"Geist Sans",
"ui-sans-serif",
"system-ui",
"-apple-system",
"BlinkMacSystemFont",
"Inter",
"Segoe UI",
"Roboto",
"sans-serif",
"Apple Color Emoji",
"Segoe UI Emoji",
"Segoe UI Symbol",
"Noto Color Emoji",
],
title: [
"Bricolage Grotesque",
"-apple-system",
"blinkmacsystemfont",
"segoe ui",
"roboto",
"oxygen",
"ubuntu",
"cantarell",
"fira",
"sans",
"droid sans",
"helvetica neue",
"sans-serif",
]
},
boxShadow: {
"shadow-floating-light": "0px 6px 12px 0px rgba(99,99,99,.06),0px 22px 22px 0px rgba(99,99,99,.05),0px 50px 30px 0px rgba(99,99,99,.03),0px 89px 36px 0px rgba(99,99,99,.01),0px 140px 39px 0px rgba(99,99,99,0)",
"inner-shadow-dark-float": "0px 1px 0px 0px hsla(0,0%,100%,.03) inset,0px 0px 0px 1px hsla(0,0%,100%,.03) inset,0px 0px 0px 1px rgba(0,0,0,.1),0px 2px 2px 0px rgba(0,0,0,.1),0px 4px 4px 0px rgba(0,0,0,.1),0px 8px 8px 0px rgba(0,0,0,.1)",
"fullscreen": "0 0 0 1px rgba(0,0,0,.08),0px 1px 1px rgba(0,0,0,.02),0px 8px 16px -4px rgba(0,0,0,.04),0px 24px 32px -8px rgba(0,0,0,.06)",
"menu": "0 0 0 1px rgba(0,0,0,.08),0px 1px 1px rgba(0,0,0,.02),0px 4px 8px -4px rgba(0,0,0,.04),0px 16px 24px -8px rgba(0,0,0,.06)",
},
keyframes: {
"fade-up": {
"0%": {
opacity: "0",
transform: "translateY(10px)",
},
"80%": {
opacity: "0.7",
},
"100%": {
opacity: "1",
transform: "translateY(0px)",
},
},
"fade-down": {
"0%": {
opacity: "0",
transform: "translateY(-10px)",
},
"80%": {
opacity: "0.6",
},
"100%": {
opacity: "1",
transform: "translateY(0px)",
},
},
shake: {
"25%": {
translate: "0.25ch 0"
},
"75%": {
translate: "-0.25ch 0"
}
},
slide: {
"100%": {
translate: "var(--destination-x) var(--destination-y);",
},
},
"multicolor": {
"0%": {
transform: "translateX(0%)"
},
"100%": {
transform: "translateX(-50%)"
}
}
},
animation: {
// Fade up and down
"fade-up": "fade-up 0.5s",
"fade-down": "fade-down 0.5s",
"shake": "shake 0.075s 8",
"multicolor": "multicolor 5s linear 0s infinite"
},
},
plugins: []
}
}

View File

@@ -3,6 +3,6 @@
"compilerOptions": {
"outDir": "dist"
},
"include": ["src", "turbo"],
"include": ["src", "tailwind.config.js", "post.config.js"],
"exclude": ["node_modules", "dist"]
}