mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-04-26 01:45:25 -04:00
add stylelint
This commit is contained in:
parent
8e89701453
commit
5b2caf4a96
@ -20,10 +20,12 @@
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
},
|
||||
"extensions": [
|
||||
"EditorConfig.EditorConfig",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"EditorConfig.EditorConfig",
|
||||
"prisma.prisma",
|
||||
"unifiedjs.vscode-mdx"
|
||||
"unifiedjs.vscode-mdx",
|
||||
"esbenp.prettier-vscode",
|
||||
"stylelint.vscode-stylelint"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -1,6 +1,4 @@
|
||||
/**
|
||||
* @type {import("prettier").Config}
|
||||
*/
|
||||
/** @type {import("prettier").Config} */
|
||||
const config = {
|
||||
singleQuote: false,
|
||||
jsxSingleQuote: false,
|
19
.stylelintrc.mjs
Normal file
19
.stylelintrc.mjs
Normal file
@ -0,0 +1,19 @@
|
||||
/* eslint-disable import/no-anonymous-default-export */
|
||||
|
||||
/** @type {import("stylelint").Config} */
|
||||
export default {
|
||||
extends: ["stylelint-config-standard", "stylelint-config-css-modules"],
|
||||
rules: {
|
||||
"selector-class-pattern": null,
|
||||
"custom-property-pattern": null,
|
||||
"media-feature-range-notation": null,
|
||||
"rule-empty-line-before": [
|
||||
"always-multi-line",
|
||||
{
|
||||
except: ["after-single-line-comment"],
|
||||
ignore: ["inside-block"],
|
||||
},
|
||||
],
|
||||
"color-hex-length": "long",
|
||||
},
|
||||
};
|
@ -58,6 +58,7 @@
|
||||
.result.success {
|
||||
color: var(--colors-success);
|
||||
}
|
||||
|
||||
.result.error {
|
||||
color: var(--colors-error);
|
||||
}
|
||||
|
@ -35,11 +35,13 @@
|
||||
white-space: nowrap;
|
||||
margin-right: 0.75em;
|
||||
}
|
||||
.meta .metaTag:before {
|
||||
|
||||
.meta .metaTag::before {
|
||||
content: "\0023"; /* cosmetically hashtagify tags */
|
||||
padding-right: 0.125em;
|
||||
color: var(--colors-light);
|
||||
}
|
||||
|
||||
.meta .metaTag:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
.section:first-of-type {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
.section:last-of-type {
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
@ -60,6 +60,7 @@
|
||||
30% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
/* pause for ~9 out of 10 seconds */
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
|
@ -11,7 +11,7 @@
|
||||
.card {
|
||||
flex-grow: 1;
|
||||
width: 370px;
|
||||
padding: 1.2em 1.2em 0.8em 1.2em;
|
||||
padding: 1.2em 1.2em 0.8em;
|
||||
border: 1px solid var(--colors-kindaLight);
|
||||
border-radius: var(--radii-corner);
|
||||
font-size: 0.9em;
|
||||
@ -32,7 +32,6 @@
|
||||
.card .meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.card .metaItem {
|
||||
@ -51,7 +50,7 @@
|
||||
}
|
||||
|
||||
.card .metaIcon {
|
||||
display: inline;
|
||||
display: inline-block;
|
||||
width: 1.25em;
|
||||
height: 1.25em;
|
||||
vertical-align: -0.25em;
|
||||
@ -61,9 +60,10 @@
|
||||
.card .metaLanguage {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 1.15em;
|
||||
height: 1.15em;
|
||||
width: 1.1em;
|
||||
height: 1.1em;
|
||||
line-height: 1;
|
||||
margin-right: 0.5em;
|
||||
border-radius: 50%;
|
||||
vertical-align: text-top;
|
||||
vertical-align: -0.2em;
|
||||
}
|
||||
|
@ -114,11 +114,7 @@ export default async function Page() {
|
||||
<div className={styles.grid}>
|
||||
{repos?.map((repo) => (
|
||||
<div key={repo.name} className={styles.card}>
|
||||
<Link
|
||||
// @ts-ignore
|
||||
href={repo.url}
|
||||
className={styles.name}
|
||||
>
|
||||
<Link href={repo.url} className={styles.name}>
|
||||
{repo.name}
|
||||
</Link>
|
||||
|
||||
@ -137,7 +133,6 @@ export default async function Page() {
|
||||
{repo.stars && repo.stars > 0 && (
|
||||
<div className={styles.metaItem}>
|
||||
<Link
|
||||
// @ts-ignore
|
||||
href={`${repo.url}/stargazers`}
|
||||
title={`${commaNumber(repo.stars)} ${repo.stars === 1 ? "star" : "stars"}`}
|
||||
plain
|
||||
@ -152,7 +147,6 @@ export default async function Page() {
|
||||
{repo.forks && repo.forks > 0 && (
|
||||
<div className={styles.metaItem}>
|
||||
<Link
|
||||
// @ts-ignore
|
||||
href={`${repo.url}/network/members`}
|
||||
title={`${commaNumber(repo.forks)} ${repo.forks === 1 ? "fork" : "forks"}`}
|
||||
plain
|
||||
|
@ -1,7 +1,7 @@
|
||||
:root {
|
||||
--colors-backgroundInner: #ffffff;
|
||||
--colors-backgroundOuter: #fcfcfc;
|
||||
--colors-backgroundHeader: rgba(252, 252, 252, 0.7);
|
||||
--colors-backgroundHeader: rgb(252 252 252 / 70%);
|
||||
--colors-text: #202020;
|
||||
--colors-mediumDark: #515151;
|
||||
--colors-medium: #5e5e5e;
|
||||
@ -11,7 +11,7 @@
|
||||
--colors-superLight: #f4f4f4;
|
||||
--colors-superDuperLight: #fbfbfb;
|
||||
--colors-link: #0e6dc2;
|
||||
--colors-linkUnderline: rgba(14, 109, 194, 0.4);
|
||||
--colors-linkUnderline: rgb(14 109 194 / 40%);
|
||||
--colors-success: #44a248;
|
||||
--colors-error: #ff1b1b;
|
||||
--colors-warning: #f78200;
|
||||
@ -33,7 +33,7 @@
|
||||
[data-theme="dark"] {
|
||||
--colors-backgroundInner: #1e1e1e;
|
||||
--colors-backgroundOuter: #252525;
|
||||
--colors-backgroundHeader: rgba(37, 37, 37, 0.85);
|
||||
--colors-backgroundHeader: rgb(37 37 37 / 85%);
|
||||
--colors-text: #f1f1f1;
|
||||
--colors-mediumDark: #d7d7d7;
|
||||
--colors-medium: #b1b1b1;
|
||||
@ -43,7 +43,7 @@
|
||||
--colors-superLight: #272727;
|
||||
--colors-superDuperLight: #1f1f1f;
|
||||
--colors-link: #88c7ff;
|
||||
--colors-linkUnderline: rgba(136, 199, 255, 0.4);
|
||||
--colors-linkUnderline: rgb(136 199 255 / 40%);
|
||||
--colors-success: #78df55;
|
||||
--colors-error: #ff5151;
|
||||
--colors-warning: #f2b702;
|
||||
|
@ -36,6 +36,7 @@
|
||||
.codeBlock.highlight :global(.token.cdata) {
|
||||
color: var(--colors-codeComment);
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.delimiter),
|
||||
.codeBlock.highlight :global(.token.boolean),
|
||||
.codeBlock.highlight :global(.token.keyword),
|
||||
@ -46,11 +47,13 @@
|
||||
.codeBlock.highlight :global(.token.url) {
|
||||
color: var(--colors-codeKeyword);
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.tag),
|
||||
.codeBlock.highlight :global(.token.builtin),
|
||||
.codeBlock.highlight :global(.token.regex) {
|
||||
color: var(--colors-codeNamespace);
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.property),
|
||||
.codeBlock.highlight :global(.token.constant),
|
||||
.codeBlock.highlight :global(.token.variable),
|
||||
@ -60,29 +63,37 @@
|
||||
.codeBlock.highlight :global(.token.char) {
|
||||
color: var(--colors-codeVariable);
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.literal-property),
|
||||
.codeBlock.highlight :global(.token.attr-name) {
|
||||
color: var(--colors-codeAttribute);
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.function) {
|
||||
color: var(--colors-codeLiteral);
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.tag .punctuation),
|
||||
.codeBlock.highlight :global(.token.attr-value .punctuation) {
|
||||
color: var(--colors-codePunctuation);
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.inserted) {
|
||||
color: var(--colors-codeAddition);
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.deleted) {
|
||||
color: var(--colors-codeDeletion);
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.url) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.bold) {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.codeBlock.highlight :global(.token.italic) {
|
||||
font-style: italic;
|
||||
}
|
||||
@ -91,9 +102,21 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 0.65em;
|
||||
background-color: var(--colors-backgroundInner);
|
||||
height: 3em;
|
||||
width: 3em;
|
||||
color: var(--colors-mediumDark);
|
||||
border: 1px solid var(--colors-kindaLight);
|
||||
border-top-right-radius: var(--radii-corner);
|
||||
border-bottom-left-radius: var(--radii-corner);
|
||||
background-color: var(--colors-backgroundHeader);
|
||||
backdrop-filter: saturate(180%) blur(5px);
|
||||
}
|
||||
|
||||
.cornerCopyButton > svg {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.cornerCopyButton:hover,
|
||||
.cornerCopyButton:focus-visible {
|
||||
color: var(--colors-link);
|
||||
}
|
||||
|
@ -1,20 +0,0 @@
|
||||
.button {
|
||||
color: var(--colors-mediumDark);
|
||||
line-height: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.button:hover,
|
||||
.button:focus-visible {
|
||||
color: var(--colors-link);
|
||||
}
|
||||
|
||||
.button.copied {
|
||||
color: var(--colors-success) !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 1.25em;
|
||||
height: 1.25em;
|
||||
vertical-align: -0.3em;
|
||||
}
|
@ -3,24 +3,18 @@
|
||||
import { forwardRef, useState, useEffect } from "react";
|
||||
import innerText from "react-innertext";
|
||||
import copy from "copy-to-clipboard";
|
||||
import clsx from "clsx";
|
||||
import { ClipboardIcon, CheckIcon } from "lucide-react";
|
||||
import type { ReactNode, Ref, ComponentPropsWithoutRef, ElementRef, MouseEventHandler } from "react";
|
||||
|
||||
import styles from "./CopyButton.module.css";
|
||||
import type { ReactNode, Ref, ComponentPropsWithoutRef, ComponentRef, MouseEventHandler } from "react";
|
||||
|
||||
export type CopyButtonProps = ComponentPropsWithoutRef<"button"> & {
|
||||
source: string | ReactNode;
|
||||
timeout?: number;
|
||||
};
|
||||
|
||||
const CopyButton = (
|
||||
{ source, timeout = 2000, className, ...rest }: CopyButtonProps,
|
||||
ref: Ref<ElementRef<"button">>
|
||||
) => {
|
||||
const CopyButton = ({ source, timeout = 2000, style, ...rest }: CopyButtonProps, ref: Ref<ComponentRef<"button">>) => {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
const handleCopy: MouseEventHandler<ElementRef<"button">> = (e) => {
|
||||
const handleCopy: MouseEventHandler<ComponentRef<"button">> = (e) => {
|
||||
// prevent unintentional double-clicks by unfocusing button
|
||||
e.currentTarget.blur();
|
||||
|
||||
@ -54,13 +48,13 @@ const CopyButton = (
|
||||
aria-label="Copy to clipboard"
|
||||
onClick={handleCopy}
|
||||
disabled={copied}
|
||||
className={clsx(styles.button, copied && styles.copied, className)}
|
||||
style={{ cursor: copied ? "default" : "pointer", ...style }}
|
||||
{...rest}
|
||||
>
|
||||
{copied ? (
|
||||
<CheckIcon size="1.25em" className={styles.icon} />
|
||||
<CheckIcon size="1.25em" style={{ stroke: "var(--colors-success)" }} />
|
||||
) : (
|
||||
<ClipboardIcon size="1.25em" className={styles.icon} />
|
||||
<ClipboardIcon size="1.25em" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
|
@ -66,6 +66,7 @@
|
||||
8% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
/* pause for ~9 out of 10 seconds */
|
||||
100% {
|
||||
transform: scale(1);
|
||||
|
@ -6,7 +6,6 @@ import MenuItem from "../MenuItem";
|
||||
import ThemeToggle from "../ThemeToggle";
|
||||
import { menuItems } from "../../lib/config/menu";
|
||||
import type { ComponentPropsWithoutRef } from "react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
|
||||
import styles from "./Menu.module.css";
|
||||
|
||||
@ -29,7 +28,10 @@ const Menu = ({ className, ...rest }: MenuProps) => {
|
||||
})}
|
||||
|
||||
<li className={styles.menuItem}>
|
||||
<MenuItem icon={ThemeToggle as LucideIcon} />
|
||||
<MenuItem
|
||||
// @ts-expect-error
|
||||
icon={ThemeToggle}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* stylelint-disable-next-line selector-type-no-unknown */
|
||||
.wrapper lite-youtube {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
@ -16,7 +16,8 @@ const compat = new FlatCompat({
|
||||
export default [
|
||||
{ ignores: ["README.md", ".next", ".vercel", "node_modules"] },
|
||||
...compat.config({
|
||||
extends: ["eslint:recommended", "next/core-web-vitals", "next/typescript"],
|
||||
plugins: ["css-modules"],
|
||||
extends: ["eslint:recommended", "next/core-web-vitals", "next/typescript", "plugin:css-modules/recommended"],
|
||||
}),
|
||||
...eslintCustomConfig,
|
||||
eslintPluginPrettierRecommended,
|
@ -77,7 +77,7 @@ const nextConfig: NextConfig = {
|
||||
|
||||
// NOTE: don't remove this, it ensures de-AMPing the site hasn't offended our google overlords too badly!
|
||||
// https://developers.google.com/search/docs/advanced/experience/remove-amp#remove-only-amp
|
||||
{ source: "/notes/:slug/amp.html", destination: "/notes/:slug/", permanent: true },
|
||||
{ source: "/notes/:slug/amp.html", destination: "/notes/:slug", permanent: true },
|
||||
|
||||
// mastodon via subdomain:
|
||||
// https://docs.joinmastodon.org/admin/config/#web_domain
|
||||
@ -107,32 +107,29 @@ const nextConfig: NextConfig = {
|
||||
permanent: true,
|
||||
},
|
||||
|
||||
// google search console has tons of 404s for images prefixed with /public... why? no clue.
|
||||
{ source: "/public/static/:path*", destination: "/static/:path*", permanent: true },
|
||||
|
||||
// remnants of previous sites/CMSes:
|
||||
{ source: "/index.xml", destination: "/feed.xml", permanent: true },
|
||||
{ source: "/feed", destination: "/feed.xml", permanent: true },
|
||||
{ source: "/rss", destination: "/feed.xml", permanent: true },
|
||||
{ source: "/blog/:path*", destination: "/notes/", permanent: true },
|
||||
{ source: "/archives/:path*", destination: "/notes/", permanent: true },
|
||||
{ source: "/blog/:path*", destination: "/notes", permanent: true },
|
||||
{ source: "/archives/:path*", destination: "/notes", permanent: true },
|
||||
{ source: "/resume", destination: "/static/resume.pdf", permanent: false },
|
||||
{ source: "/resume.pdf", destination: "/static/resume.pdf", permanent: false },
|
||||
|
||||
// WordPress permalinks:
|
||||
{
|
||||
source: "/2016/02/28/millenial-with-hillary-clinton",
|
||||
destination: "/notes/millenial-with-hillary-clinton/",
|
||||
destination: "/notes/millenial-with-hillary-clinton",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/2018/12/04/how-to-shrink-linux-virtual-disk-vmware",
|
||||
destination: "/notes/how-to-shrink-linux-virtual-disk-vmware/",
|
||||
destination: "/notes/how-to-shrink-linux-virtual-disk-vmware",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/2018/12/10/cool-bash-tricks-for-your-terminal-dotfiles",
|
||||
destination: "/notes/cool-bash-tricks-for-your-terminal-dotfiles/",
|
||||
destination: "/notes/cool-bash-tricks-for-your-terminal-dotfiles",
|
||||
permanent: true,
|
||||
},
|
||||
],
|
||||
|
17
package.json
17
package.json
@ -9,7 +9,6 @@
|
||||
"email": "jake@jarv.is",
|
||||
"url": "https://github.com/jakejarvis"
|
||||
},
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "next dev -H 0.0.0.0",
|
||||
"build": "next build",
|
||||
@ -23,9 +22,9 @@
|
||||
"@giscus/react": "^3.1.0",
|
||||
"@mdx-js/loader": "^3.1.0",
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"@next/bundle-analyzer": "15.3.0-canary.0",
|
||||
"@next/mdx": "15.3.0-canary.0",
|
||||
"@next/third-parties": "15.3.0-canary.0",
|
||||
"@next/bundle-analyzer": "15.3.0-canary.1",
|
||||
"@next/mdx": "15.3.0-canary.1",
|
||||
"@next/third-parties": "15.3.0-canary.1",
|
||||
"@octokit/graphql": "^8.2.1",
|
||||
"@octokit/graphql-schema": "^15.26.0",
|
||||
"@prisma/client": "^6.5.0",
|
||||
@ -37,7 +36,7 @@
|
||||
"feed": "^4.2.2",
|
||||
"lucide-react": "0.479.0",
|
||||
"modern-normalize": "^3.0.1",
|
||||
"next": "15.3.0-canary.0",
|
||||
"next": "15.3.0-canary.1",
|
||||
"obj-str": "^1.1.0",
|
||||
"p-map": "^7.0.3",
|
||||
"p-memoize": "^7.1.1",
|
||||
@ -78,8 +77,9 @@
|
||||
"@types/react-is": "^19.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.22.0",
|
||||
"eslint-config-next": "15.3.0-canary.0",
|
||||
"eslint-config-next": "15.3.0-canary.1",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"eslint-plugin-css-modules": "^2.12.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-mdx": "^3.2.0",
|
||||
@ -91,6 +91,10 @@
|
||||
"prisma": "^6.5.0",
|
||||
"schema-dts": "^1.1.5",
|
||||
"simple-git-hooks": "^2.11.1",
|
||||
"stylelint": "^16.15.0",
|
||||
"stylelint-config-css-modules": "^4.4.0",
|
||||
"stylelint-config-recommended": "^15.0.0",
|
||||
"stylelint-config-standard": "^37.0.0",
|
||||
"typescript": "5.8.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
@ -112,6 +116,7 @@
|
||||
"eslint"
|
||||
],
|
||||
"*.css": [
|
||||
"stylelint",
|
||||
"prettier --check"
|
||||
]
|
||||
},
|
||||
|
714
pnpm-lock.yaml
generated
714
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
@ -24,13 +20,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user