samantha's personal website!~ ✨ samanthanguyen.me
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

design polishes

+440 -121
+21 -14
eslint.config.js
··· 1 1 import js from '@eslint/js' 2 + import stylistic from '@stylistic/eslint-plugin' 2 3 import svelte from 'eslint-plugin-svelte' 3 4 import globals from 'globals' 4 5 import ts from 'typescript-eslint' ··· 6 7 export default ts.config( 7 8 { 8 9 ignores: [ 9 - '.svelte-kit/' 10 - ] 10 + '.svelte-kit/', 11 + ], 11 12 }, 13 + stylistic.configs.customize({ 14 + indent: 'tab', 15 + quote: 'single', 16 + semi: false, 17 + jsx: false, 18 + }), 12 19 js.configs.recommended, 13 20 ...ts.configs.recommended, 14 - ...svelte.configs["flat/recommended"], 21 + ...svelte.configs['flat/recommended'], 15 22 { 16 23 rules: { 17 - "no-explicit-any": "off", 24 + 'no-explicit-any': 'off', 18 25 '@typescript-eslint/no-explicit-any': 'off', 19 - } 26 + }, 20 27 }, 21 28 { 22 29 languageOptions: { 23 30 globals: { 24 31 ...globals.browser, 25 - ...globals.node 26 - } 27 - } 32 + ...globals.node, 33 + }, 34 + }, 28 35 }, 29 36 { 30 - files: ["**/*.svelte"], 37 + files: ['**/*.svelte'], 31 38 languageOptions: { 32 39 parserOptions: { 33 - parser: ts.parser 34 - } 35 - } 36 - } 37 - ); 40 + parser: ts.parser, 41 + }, 42 + }, 43 + }, 44 + )
+181 -15
package-lock.json
··· 9 9 "version": "0.0.1", 10 10 "dependencies": { 11 11 "@fontsource-variable/domine": "^5.1.0", 12 + "@fontsource-variable/geist": "^5.0.1", 12 13 "@fontsource-variable/public-sans": "^5.1.1", 13 14 "@tabler/icons-svelte": "^3.26.0", 14 15 "@wooorm/starry-night": "^3.5.0", 16 + "bits-ui": "^1.0.0-next.77", 15 17 "hast-util-to-html": "^9.0.4", 16 18 "tailwind-merge": "^2.5.5" 17 19 }, 18 20 "devDependencies": { 21 + "@stylistic/eslint-plugin": "^2.12.1", 19 22 "@sveltejs/adapter-auto": "^3.0.0", 20 23 "@sveltejs/adapter-cloudflare": "^4.8.0", 21 24 "@sveltejs/kit": "^2.9.0", ··· 25 28 "eslint-plugin-svelte": "^2.36.0", 26 29 "globals": "^15.0.0", 27 30 "mdsvex": "^0.11.2", 28 - "svelte": "^5.0.0", 31 + "svelte": "^5.16.5", 29 32 "svelte-check": "^4.0.0", 30 33 "tailwindcss": "^3.4.9", 31 34 "typescript": "^5.0.0", ··· 781 784 "node": ">=14" 782 785 } 783 786 }, 787 + "node_modules/@floating-ui/core": { 788 + "version": "1.6.8", 789 + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", 790 + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", 791 + "license": "MIT", 792 + "dependencies": { 793 + "@floating-ui/utils": "^0.2.8" 794 + } 795 + }, 796 + "node_modules/@floating-ui/dom": { 797 + "version": "1.6.12", 798 + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz", 799 + "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==", 800 + "license": "MIT", 801 + "dependencies": { 802 + "@floating-ui/core": "^1.6.0", 803 + "@floating-ui/utils": "^0.2.8" 804 + } 805 + }, 806 + "node_modules/@floating-ui/utils": { 807 + "version": "0.2.8", 808 + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", 809 + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", 810 + "license": "MIT" 811 + }, 784 812 "node_modules/@fontsource-variable/domine": { 785 813 "version": "5.1.0", 786 814 "resolved": "https://registry.npmjs.org/@fontsource-variable/domine/-/domine-5.1.0.tgz", 787 815 "integrity": "sha512-2dhYul51BpyFuinCKIsVzZ4A41tb9GiThGho06r7QARofHvkSYsM4iLKPoxtOoDYyqkDHv4emKDMiVsbxlKQ+g==", 816 + "license": "OFL-1.1" 817 + }, 818 + "node_modules/@fontsource-variable/geist": { 819 + "version": "5.0.1", 820 + "resolved": "https://registry.npmjs.org/@fontsource-variable/geist/-/geist-5.0.1.tgz", 821 + "integrity": "sha512-TDSL3iy47DFGM8go1hejvloGaTFD7bxTjh9Oy9KXpC9z55eBcTueziPlyEqVVb9Sn1OkiW4lPgXQkc+BNOzFvA==", 788 822 "license": "OFL-1.1" 789 823 }, 790 824 "node_modules/@fontsource-variable/public-sans": { ··· 857 891 "funding": { 858 892 "type": "github", 859 893 "url": "https://github.com/sponsors/nzakas" 894 + } 895 + }, 896 + "node_modules/@internationalized/date": { 897 + "version": "3.6.0", 898 + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.6.0.tgz", 899 + "integrity": "sha512-+z6ti+CcJnRlLHok/emGEsWQhe7kfSmEW+/6qCzvKY67YPh7YOBfvc7+/+NXq+zJlbArg30tYpqLjNgcAYv2YQ==", 900 + "license": "Apache-2.0", 901 + "dependencies": { 902 + "@swc/helpers": "^0.5.0" 860 903 } 861 904 }, 862 905 "node_modules/@isaacs/cliui": { ··· 1247 1290 "win32" 1248 1291 ] 1249 1292 }, 1293 + "node_modules/@stylistic/eslint-plugin": { 1294 + "version": "2.12.1", 1295 + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.12.1.tgz", 1296 + "integrity": "sha512-fubZKIHSPuo07FgRTn6S4Nl0uXPRPYVNpyZzIDGfp7Fny6JjNus6kReLD7NI380JXi4HtUTSOZ34LBuNPO1XLQ==", 1297 + "dev": true, 1298 + "license": "MIT", 1299 + "dependencies": { 1300 + "@typescript-eslint/utils": "^8.13.0", 1301 + "eslint-visitor-keys": "^4.2.0", 1302 + "espree": "^10.3.0", 1303 + "estraverse": "^5.3.0", 1304 + "picomatch": "^4.0.2" 1305 + }, 1306 + "engines": { 1307 + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1308 + }, 1309 + "peerDependencies": { 1310 + "eslint": ">=8.40.0" 1311 + } 1312 + }, 1250 1313 "node_modules/@sveltejs/adapter-auto": { 1251 1314 "version": "3.3.1", 1252 1315 "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.3.1.tgz", ··· 1347 1410 "@sveltejs/vite-plugin-svelte": "^5.0.0", 1348 1411 "svelte": "^5.0.0", 1349 1412 "vite": "^6.0.0" 1413 + } 1414 + }, 1415 + "node_modules/@swc/helpers": { 1416 + "version": "0.5.15", 1417 + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", 1418 + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", 1419 + "license": "Apache-2.0", 1420 + "dependencies": { 1421 + "tslib": "^2.8.0" 1350 1422 } 1351 1423 }, 1352 1424 "node_modules/@tabler/icons": { ··· 1893 1965 }, 1894 1966 "funding": { 1895 1967 "url": "https://github.com/sponsors/sindresorhus" 1968 + } 1969 + }, 1970 + "node_modules/bits-ui": { 1971 + "version": "1.0.0-next.77", 1972 + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-1.0.0-next.77.tgz", 1973 + "integrity": "sha512-IV0AyVEvsRkXv4s/fl4iea5E9W2b9EBf98s9mRMKMc1xHxM9MmtM2r6MZMqftHQ/c+gHTIt3A9EKuTlh7uay8w==", 1974 + "license": "MIT", 1975 + "dependencies": { 1976 + "@floating-ui/core": "^1.6.4", 1977 + "@floating-ui/dom": "^1.6.7", 1978 + "@internationalized/date": "^3.5.6", 1979 + "esm-env": "^1.1.2", 1980 + "runed": "^0.22.0", 1981 + "svelte-toolbelt": "^0.7.0" 1982 + }, 1983 + "engines": { 1984 + "node": ">=18", 1985 + "pnpm": ">=8.7.0" 1986 + }, 1987 + "funding": { 1988 + "url": "https://github.com/sponsors/huntabyte" 1989 + }, 1990 + "peerDependencies": { 1991 + "svelte": "^5.11.0" 1896 1992 } 1897 1993 }, 1898 1994 "node_modules/blake3-wasm": { ··· 2076 2172 "url": "https://paulmillr.com/funding/" 2077 2173 } 2078 2174 }, 2175 + "node_modules/clsx": { 2176 + "version": "2.1.1", 2177 + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", 2178 + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", 2179 + "license": "MIT", 2180 + "engines": { 2181 + "node": ">=6" 2182 + } 2183 + }, 2079 2184 "node_modules/color-convert": { 2080 2185 "version": "2.0.1", 2081 2186 "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", ··· 2530 2635 } 2531 2636 }, 2532 2637 "node_modules/esrap": { 2533 - "version": "1.2.3", 2534 - "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.2.3.tgz", 2535 - "integrity": "sha512-ZlQmCCK+n7SGoqo7DnfKaP1sJZa49P01/dXzmjCASSo04p72w8EksT2NMK8CEX8DhKsfJXANioIw8VyHNsBfvQ==", 2638 + "version": "1.3.2", 2639 + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.3.2.tgz", 2640 + "integrity": "sha512-C4PXusxYhFT98GjLSmb20k9PREuUdporer50dhzGuJu9IJXktbMddVCMLAERl5dAHyAi73GWWCE4FVHGP1794g==", 2536 2641 "license": "MIT", 2537 2642 "dependencies": { 2538 - "@jridgewell/sourcemap-codec": "^1.4.15", 2539 - "@types/estree": "^1.0.1" 2643 + "@jridgewell/sourcemap-codec": "^1.4.15" 2540 2644 } 2541 2645 }, 2542 2646 "node_modules/esrecurse": { ··· 3025 3129 "engines": { 3026 3130 "node": ">=0.8.19" 3027 3131 } 3132 + }, 3133 + "node_modules/inline-style-parser": { 3134 + "version": "0.2.4", 3135 + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", 3136 + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", 3137 + "license": "MIT" 3028 3138 }, 3029 3139 "node_modules/is-binary-path": { 3030 3140 "version": "2.1.0", ··· 3798 3908 "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", 3799 3909 "dev": true, 3800 3910 "license": "MIT", 3801 - "optional": true, 3802 - "peer": true, 3803 3911 "engines": { 3804 3912 "node": ">=12" 3805 3913 }, ··· 4284 4392 "queue-microtask": "^1.2.2" 4285 4393 } 4286 4394 }, 4395 + "node_modules/runed": { 4396 + "version": "0.22.0", 4397 + "resolved": "https://registry.npmjs.org/runed/-/runed-0.22.0.tgz", 4398 + "integrity": "sha512-ZWVXWhOr0P5xdNgtviz6D1ivLUDWKLCbeC5SUEJ3zBkqLReVqWHenFxMNFeFaiC5bfxhFxyxzyzB+98uYFtwdA==", 4399 + "funding": [ 4400 + "https://github.com/sponsors/huntabyte", 4401 + "https://github.com/sponsors/tglide" 4402 + ], 4403 + "dependencies": { 4404 + "esm-env": "^1.0.0" 4405 + }, 4406 + "peerDependencies": { 4407 + "svelte": "^5.7.0" 4408 + } 4409 + }, 4287 4410 "node_modules/sade": { 4288 4411 "version": "1.8.1", 4289 4412 "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", ··· 4578 4701 "url": "https://github.com/sponsors/sindresorhus" 4579 4702 } 4580 4703 }, 4704 + "node_modules/style-to-object": { 4705 + "version": "1.0.8", 4706 + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", 4707 + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", 4708 + "license": "MIT", 4709 + "dependencies": { 4710 + "inline-style-parser": "0.2.4" 4711 + } 4712 + }, 4581 4713 "node_modules/sucrase": { 4582 4714 "version": "3.35.0", 4583 4715 "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", ··· 4628 4760 } 4629 4761 }, 4630 4762 "node_modules/svelte": { 4631 - "version": "5.14.1", 4632 - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.14.1.tgz", 4633 - "integrity": "sha512-DET9IJw6LUStRnu5rTXnlBs1fsJt417C9QXE8J+gIEWc4IsqxcJsa3OYUsf7ZJmDQbaBudcp4pxI7Za0NR1QYg==", 4763 + "version": "5.16.5", 4764 + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.16.5.tgz", 4765 + "integrity": "sha512-zTG45crJUGjNYQgmQ0YDxFJ7ge1O6ZwevPxGgGOxuMOXOQhcH9LC9GEx2JS9/BlkhxdsO8ETofQ76ouFwDVpCQ==", 4634 4766 "license": "MIT", 4635 4767 "dependencies": { 4636 4768 "@ampproject/remapping": "^2.3.0", ··· 4640 4772 "acorn-typescript": "^1.4.13", 4641 4773 "aria-query": "^5.3.1", 4642 4774 "axobject-query": "^4.1.0", 4775 + "clsx": "^2.1.1", 4643 4776 "esm-env": "^1.2.1", 4644 - "esrap": "^1.2.3", 4777 + "esrap": "^1.3.2", 4645 4778 "is-reference": "^3.0.3", 4646 4779 "locate-character": "^3.0.0", 4647 4780 "magic-string": "^0.30.11", ··· 4749 4882 }, 4750 4883 "funding": { 4751 4884 "url": "https://opencollective.com/eslint" 4885 + } 4886 + }, 4887 + "node_modules/svelte-toolbelt": { 4888 + "version": "0.7.0", 4889 + "resolved": "https://registry.npmjs.org/svelte-toolbelt/-/svelte-toolbelt-0.7.0.tgz", 4890 + "integrity": "sha512-i/Tv4NwAWWqJnK5H0F8y/ubDnogDYlwwyzKhrspTUFzrFuGnYshqd2g4/R43ds841wmaFiSW/HsdsdWhPOlrAA==", 4891 + "funding": [ 4892 + "https://github.com/sponsors/huntabyte" 4893 + ], 4894 + "dependencies": { 4895 + "clsx": "^2.1.1", 4896 + "runed": "^0.20.0", 4897 + "style-to-object": "^1.0.8" 4898 + }, 4899 + "engines": { 4900 + "node": ">=18", 4901 + "pnpm": ">=8.7.0" 4902 + }, 4903 + "peerDependencies": { 4904 + "svelte": "^5.0.0" 4905 + } 4906 + }, 4907 + "node_modules/svelte-toolbelt/node_modules/runed": { 4908 + "version": "0.20.0", 4909 + "resolved": "https://registry.npmjs.org/runed/-/runed-0.20.0.tgz", 4910 + "integrity": "sha512-YqPxaUdWL5nUXuSF+/v8a+NkVN8TGyEGbQwTA25fLY35MR/2bvZ1c6sCbudoo1kT4CAJPh4kUkcgGVxW127WKw==", 4911 + "funding": [ 4912 + "https://github.com/sponsors/huntabyte", 4913 + "https://github.com/sponsors/tglide" 4914 + ], 4915 + "dependencies": { 4916 + "esm-env": "^1.0.0" 4917 + }, 4918 + "peerDependencies": { 4919 + "svelte": "^5.7.0" 4752 4920 } 4753 4921 }, 4754 4922 "node_modules/tailwind-merge": { ··· 5003 5171 "version": "2.8.1", 5004 5172 "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 5005 5173 "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 5006 - "dev": true, 5007 - "license": "0BSD", 5008 - "peer": true 5174 + "license": "0BSD" 5009 5175 }, 5010 5176 "node_modules/type-check": { 5011 5177 "version": "0.4.0",
+4 -1
package.json
··· 13 13 "fix": "eslint . --fix" 14 14 }, 15 15 "devDependencies": { 16 + "@stylistic/eslint-plugin": "^2.12.1", 16 17 "@sveltejs/adapter-auto": "^3.0.0", 17 18 "@sveltejs/adapter-cloudflare": "^4.8.0", 18 19 "@sveltejs/kit": "^2.9.0", ··· 22 23 "eslint-plugin-svelte": "^2.36.0", 23 24 "globals": "^15.0.0", 24 25 "mdsvex": "^0.11.2", 25 - "svelte": "^5.0.0", 26 + "svelte": "^5.16.5", 26 27 "svelte-check": "^4.0.0", 27 28 "tailwindcss": "^3.4.9", 28 29 "typescript": "^5.0.0", ··· 31 32 }, 32 33 "dependencies": { 33 34 "@fontsource-variable/domine": "^5.1.0", 35 + "@fontsource-variable/geist": "^5.0.1", 34 36 "@fontsource-variable/public-sans": "^5.1.1", 35 37 "@tabler/icons-svelte": "^3.26.0", 36 38 "@wooorm/starry-night": "^3.5.0", 39 + "bits-ui": "^1.0.0-next.77", 37 40 "hast-util-to-html": "^9.0.4", 38 41 "tailwind-merge": "^2.5.5" 39 42 }
+3 -3
postcss.config.js
··· 1 1 export default { 2 2 plugins: { 3 3 tailwindcss: {}, 4 - autoprefixer: {} 5 - } 6 - }; 4 + autoprefixer: {}, 5 + }, 6 + }
+1 -1
src/app.d.ts
··· 10 10 } 11 11 } 12 12 13 - export {}; 13 + export {}
+5 -4
src/app.html
··· 2 2 <html lang="en"> 3 3 <head> 4 4 <meta charset="utf-8" /> 5 + <meta name="viewport" content="width=device-width, initial-scale=1" /> 6 + <meta name="theme-color" content="#f6f8f6" media="(prefers-color-scheme: light)"> 7 + <meta name="theme-color" content="#18181b" media="(prefers-color-scheme: dark)"> 5 8 <link rel="icon" href="%sveltekit.assets%/favicon.ico" /> 6 9 <link type="image/png" sizes="16x16" rel="icon" href="%sveltekit.assets%/favicon-16x16.png"> 7 10 <link type="image/png" sizes="32x32" rel="icon" href="%sveltekit.assets%/favicon-32x32.png"> ··· 9 12 <link type="image/png" sizes="32x32" rel="icon" href="%sveltekit.assets%/android-chrome-512x512.png"> 10 13 <link type="image/png" sizes="180x180" rel="apple-touch-icon" href="%sveltekit.assets%/apple-touch-icon.png"> 11 14 <link type="image/png" sizes="180x180" rel="apple-touch-icon-precomposed" href="%sveltekit.assets%/apple-touch-icon-precomposed.png"> 12 - <meta name="viewport" content="width=device-width, initial-scale=1" /> 13 - <meta name="theme-color" content="#EDE3E9" media="(prefers-color-scheme: light)"> 14 - <meta name="theme-color" content="#070707" media="(prefers-color-scheme: dark)"> 15 + <link type="font/woff2" as="font" rel="preload" href="%sveltekit.assets%/fonts/DepartureMono-Regular.woff2" crossorigin="anonymous"> 15 16 %sveltekit.head% 16 17 </head> 17 - <body data-sveltekit-preload-data="hover" class="bg-[#EDE3E9] dark:bg-neutral-900 text-[#110b11] dark:text-neutral-300"> 18 + <body data-sveltekit-preload-data="hover" class="bg-[#f6f8f6] dark:bg-zinc-900 text-[#110b11] dark:text-zinc-100"> 18 19 <div style="display: contents">%sveltekit.body%</div> 19 20 </body> 20 21 </html>
+2 -2
src/lib/Quote.svelte
··· 1 1 <script lang="ts"> 2 2 let { children, author } = $props() 3 3 </script> 4 - <blockquote class="flex flex-col gap-2 italic font-sans text-sm"> 4 + <blockquote class="flex flex-col gap-2 italic text-xs lg:text-sm"> 5 5 <p> 6 - {"\""}{@render children()}{"\""} 6 + {'"'}{@render children()}{'"'} 7 7 </p> 8 8 <cite class="text-right">—{author}</cite> 9 9 </blockquote>
src/lib/Time.svelte src/lib/datetime/Time.svelte
+1 -1
src/lib/dates.ts src/lib/datetime/dates.ts
··· 43 43 export const formatAttribute = ( 44 44 d: Date, 45 45 timeStyle: Intl.DateTimeFormatOptions['timeStyle'], 46 - dateStyle: Intl.DateTimeFormatOptions['dateStyle'] 46 + dateStyle: Intl.DateTimeFormatOptions['dateStyle'], 47 47 ): string => { 48 48 const kind = getKind(timeStyle, dateStyle) 49 49 switch (kind) {
+43 -32
src/lib/layout/Footer.svelte
··· 1 1 <script lang="ts"> 2 - import { IconBrandBluesky, IconBrandGithubFilled, IconMailFilled } from '@tabler/icons-svelte' 3 - import { twJoin } from 'tailwind-merge' 4 - import ExternalLink from '$lib/ExternalLink.svelte' 2 + import ExternalLink from '$lib/ExternalLink.svelte' 3 + import { IconArrowUpRight } from '@tabler/icons-svelte' 4 + 5 + const socials = [ 6 + { 7 + href: 'mailto:contact@samanthanguyen.me', 8 + title: 'Email me at contact@samanthanguyen.me', 9 + text: 'Email', 10 + }, 11 + { 12 + href: 'https://bsky.app/profile/samanthanguyen.me', 13 + title: '@samanthanguyen.me on Bluesky', 14 + text: 'Bluesky', 15 + }, 16 + { 17 + href: 'https://github.com/neoncitylights', 18 + title: '@neoncitylights on GitHub', 19 + text: 'GitHub', 20 + }, 21 + ] 5 22 </script> 6 23 7 - <div class="h-0 lg:h-[1px] bg-separator-light dark:bg-separator-dark"></div> 8 - <footer class={twJoin( 9 - "mt-[-1rem] lg:mt-0 flex flex-row justify-end", 10 - )}> 11 - <span class="flex flex-row gap-4"> 12 - <ExternalLink 13 - href="mailto:contact@samanthanguyen.me" 14 - title="Email me at contact@samanthanguyen.me"> 15 - <IconMailFilled 16 - size={32} 17 - stroke={1} 18 - class={twJoin("fill-[#CFB4C5] dark:fill-neutral-300")} /> 19 - </ExternalLink> 20 - <ExternalLink 21 - href="https://bsky.app/profile/samanthanguyen.me" 22 - title="@samanthanguyen.me on Bluesky"> 23 - <IconBrandBluesky 24 - size={32} 25 - stroke={1} 26 - class={twJoin("text-[#CFB4C5] fill-[#CFB4C5]", "dark:text-neutral-300 dark:fill-neutral-300")} /> 27 - </ExternalLink> 28 - <ExternalLink 29 - href="https://github.com/neoncitylights" 30 - title="@neoncitylights on GitHub"> 31 - <IconBrandGithubFilled 32 - size={32} 33 - stroke={1} 34 - class={twJoin("fill-[#CFB4C5]", "dark:fill-neutral-300")} /> 35 - </ExternalLink> 24 + <footer class={[ 25 + 'flex flex-col lg:gap-6', 26 + 'pt-4 border-t-2 border-t-zinc-950 dark:border-t-zinc-300', 27 + 'font-mono' 28 + ]}> 29 + <span class={[ 30 + 'flex flex-row gap-4 lg:gap-6 justify-end', 31 + '', 32 + ]}> 33 + {#each socials as link} 34 + <ExternalLink 35 + href={link.href} 36 + title={link.title} 37 + class={[ 38 + 'flex flex-row items-center gap-0.5 lg:gap-1', 39 + 'text-xs lg:text-sm font-mono', 40 + 'hover:bg-zinc-900 hover:text-zinc-100', 41 + 'dark:hover:bg-zinc-100 dark:hover:text-zinc-900', 42 + ]} 43 + > 44 + {link.text}<IconArrowUpRight size={20} /> 45 + </ExternalLink> 46 + {/each} 36 47 </span> 37 48 </footer>
+10 -14
src/lib/layout/Header.svelte
··· 1 - <script> 2 - import { twJoin } from 'tailwind-merge' 3 - </script> 4 - 5 - <header> 6 - <hgroup class="flex flex-col gap-1 lg:gap-2"> 7 - <h1 class={twJoin( 8 - "pt-12 flex items-end", 9 - "text-2xl lg:text-4xl font-domine font-bold lg:font-medium", 10 - )}> 1 + <header class="pt-12"> 2 + <hgroup class="flex flex-col gap-1"> 3 + <h1 class={[ 4 + 'text-2xl lg:text-4xl', 5 + 'font-mono font-bold lg:font-medium', 6 + ]}> 11 7 samanthanguyen.me 12 8 </h1> 13 - <p class={twJoin( 14 - "text-sm lg:text-base dark:text-zinc-500", 15 - "font-domine", 16 - )}> 9 + <p class={[ 10 + 'font-mono text-balance text-xs lg:text-base', 11 + 'dark:text-zinc-400', 12 + ]}> 17 13 she / her. artist, software engineer, aspiring computer scientist 18 14 </p> 19 15 </hgroup>
+10
src/lib/palette/CommandGroupHeading.svelte
··· 1 + <script lang='ts'> 2 + import { Command } from 'bits-ui' 3 + let { children } = $props() 4 + </script> 5 + 6 + <Command.GroupHeading 7 + class="px-3 pb-2 pt-4 text-xs text-muted-foreground" 8 + > 9 + {@render children()} 10 + </Command.GroupHeading>
+19
src/lib/palette/CommandItem.svelte
··· 1 + <script lang='ts'> 2 + import { Command } from 'bits-ui' 3 + type Props = { 4 + children: any 5 + keywords?: string[] 6 + } 7 + let { children, keywords = [] }: Props = $props() 8 + </script> 9 + 10 + <Command.Item 11 + class={[ 12 + 'flex items-center gap-2 h-10 px-3 py-2.5', 13 + 'cursor-pointer select-none text-sm capitalize', 14 + 'outline-none data-[selected]:bg-muted', 15 + ]} 16 + keywords={keywords} 17 + > 18 + {@render children()} 19 + </Command.Item>
+78
src/lib/palette/CommandPalette.svelte
··· 1 + <script> 2 + import { Command, Dialog } from 'bits-ui' 3 + import { twJoin } from 'tailwind-merge' 4 + import CommandItem from './CommandItem.svelte' 5 + import CommandGroupHeading from './CommandGroupHeading.svelte' 6 + let { open = $bindable() } = $props() 7 + </script> 8 + 9 + <Dialog.Root bind:open={open}> 10 + <Dialog.Portal> 11 + <Dialog.Overlay class={twJoin( 12 + 'fixed inset-0 z-50', 13 + 'data-[state=open]:animate-in', 14 + 'data-[state=closed]:animate-out', 15 + 'data-[state=closed]:fade-out-0', 16 + 'data-[state=open]:fade-in-0', 17 + )} /> 18 + </Dialog.Portal> 19 + <Dialog.Content class={twJoin( 20 + 'fixed left-[50%] top-[50%] z-50 w-full', 21 + 'max-w-[94%] translate-x-[-50%] translate-y-[-50%]', 22 + 'rounded-card-lg bg-background shadow-popover outline-none', 23 + 'data-[state=open]:animate-in', 24 + 'data-[state=closed]:animate-out', 25 + 'data-[state=closed]:fade-out-0', 26 + 'data-[state=open]:fade-in-0', 27 + 'data-[state=closed]:zoom-out-95', 28 + 'data-[state=open]:zoom-in-95', 29 + 'data-[state=closed]:slide-out-to-left-1/2', 30 + 'data-[state=closed]:slide-out-to-top-[48%]', 31 + 'data-[state=open]:slide-in-from-left-1/2', 32 + 'data-[state=open]:slide-in-from-top-[48%]', 33 + 'sm:max-w-[490px] md:w-full', 34 + )}> 35 + <Command.Root class={twJoin( 36 + 'flex h-full w-full flex-col self-start', 37 + 'divide-y divide-border overflow-hidden', 38 + 'rounded-xl border border-muted bg-slate-100', 39 + )}> 40 + <Command.Input 41 + class={twJoin( 42 + 'inline-flex h-input w-[296px] p-4', 43 + 'truncate text-sm', 44 + 'bg-background transition-colors placeholder:text-foreground-alt/50', 45 + 'focus-override focus:outline-none focus:ring-0', 46 + 'appearance-none bg-[unset]', 47 + )} 48 + placeholder="Search for something..." 49 + /> 50 + <Command.List class="max-h-[280px] overflow-y-auto overflow-x-hidden px-2 pb-2"> 51 + <Command.Viewport> 52 + <Command.Empty 53 + class="flex w-full items-center justify-center pb-6 pt-8 text-sm text-muted-foreground" 54 + > 55 + No results found. 56 + </Command.Empty> 57 + <Command.Group> 58 + <CommandGroupHeading>Suggestions</CommandGroupHeading> 59 + <Command.GroupItems> 60 + <CommandItem keywords={['getting started', 'tutorial']}>Introduction</CommandItem> 61 + <CommandItem keywords={['child', 'custom element', 'snippets']}>Delegation</CommandItem> 62 + <CommandItem keywords={['css', 'theme', 'colors', 'fonts', 'tailwind']}>Styling</CommandItem> 63 + </Command.GroupItems> 64 + </Command.Group> 65 + <Command.Separator /> 66 + <Command.Group> 67 + <CommandGroupHeading>Components</CommandGroupHeading> 68 + <Command.GroupItems> 69 + <CommandItem keywords={['dates', 'times']}>Calendar</CommandItem> 70 + <CommandItem keywords={['buttons', 'forms']}>Radio Group</CommandItem> 71 + <CommandItem keywords={['inputs', 'text', 'autocomplete']}>Combobox</CommandItem> 72 + </Command.GroupItems> 73 + </Command.Group> 74 + </Command.Viewport> 75 + </Command.List> 76 + </Command.Root> 77 + </Dialog.Content> 78 + </Dialog.Root>
+34 -13
src/routes/+layout.svelte
··· 1 1 <script lang="ts"> 2 - import "../app.css"; 3 - import "@fontsource-variable/public-sans"; 4 - import '@fontsource-variable/domine'; // Supports weights 400-700 5 - import { twJoin } from "tailwind-merge"; 6 - let { children } = $props(); 2 + import '../app.css' 3 + import '@fontsource-variable/public-sans' 4 + import '@fontsource-variable/domine' 5 + // import CommandPalette from '$lib/palette/CommandPalette.svelte' 6 + let { children } = $props() 7 + // let dialogOpen = $state(false) 8 + // const onKeyDown = (e: KeyboardEvent) => { 9 + // if (e.key === 'k' && (e.metaKey || e.ctrlKey)) { 10 + // e.preventDefault() 11 + // dialogOpen = true 12 + // } 13 + // } 7 14 </script> 8 15 9 16 <svelte:head> 10 17 <title>samanthanguyen.me</title> 11 - <meta name="description" content="Samantha Nguyen. An artist, software engineer, and aspiring computer scientist" /> 18 + <meta 19 + name="description" 20 + content="Samantha Nguyen. An artist, software engineer, and aspiring computer scientist" 21 + /> 12 22 </svelte:head> 23 + <!-- <svelte:document onkeydown={onKeyDown} /> 24 + <CommandPalette bind:open={dialogOpen} /> --> 13 25 14 - <div class={twJoin( 15 - "mx-8 max-w-lg", 16 - "lg:mx-auto lg:max-w-xl", 17 - "flex flex-col gap-6 lg:gap-8", 18 - "selection:bg-pink-200 selection:text-pink-700", 19 - "dark:selection:bg-white dark:selection:text-zinc-950" 20 - )}> 26 + <div 27 + class={[ 28 + 'mx-8 max-w-lg', 29 + 'lg:mx-auto lg:max-w-xl', 30 + 'flex flex-col gap-6 lg:gap-8', 31 + ]} 32 + > 21 33 {@render children()} 22 34 </div> 35 + 36 + <style> 37 + @font-face { 38 + font-family: 'Departure Mono'; 39 + font-style: normal; 40 + font-weight: 400; 41 + src: local('Departure Mono'), url('/fonts/DepartureMono-Regular.woff2') format('woff2'); 42 + } 43 + </style>
+13 -9
src/routes/+page.svelte
··· 2 2 import Footer from '$lib/layout/Footer.svelte' 3 3 import Header from '$lib/layout/Header.svelte' 4 4 import Quote from '$lib/Quote.svelte' 5 - import { twJoin } from 'tailwind-merge' 6 5 </script> 7 6 8 7 <Header /> 9 - <main class="flex flex-col gap-8 lg:gap-12"> 10 - <span class={twJoin( 11 - "flex flex-col gap-6 lg:gap-8", 12 - "font-domine text-sm lg:text-base text-left lg:text-balance" 13 - )}> 8 + <main class="flex flex-col gap-8 lg:gap-12 font-mono"> 9 + <span class={[ 10 + 'flex flex-col gap-6 lg:gap-8', 11 + 'text-xs leading-snug', 12 + 'lg:text-base lg:leading-tight', 13 + 'text-balance', 14 + ]}> 14 15 <p> 15 16 I've most recently worked as a Research Assistant 16 17 at the Security & Privacy Research Lab 17 18 of UT Arlington, from May 2024 to December 2024. 18 19 My main interests mainly revolve in low-level programming, 19 - including compiler design, programming languages, operating 20 - systems, and graphics programming. 20 + including 21 + <span class="bg-yellow-400 text-zinc-900">compiler design</span>, 22 + <span class="bg-blue-600 text-zinc-100">programming languages</span>, 23 + <span class="bg-red-600 text-zinc-100">operating systems</span>, and 24 + <span class="bg-zinc-900 text-zinc-100 dark:bg-zinc-100 dark:text-zinc-900">graphics programming</span>. 21 25 </p> 22 26 <p> 23 27 Besides programming, I've enjoyed fine arts since I was a child, 24 28 and hope to one day work in a field where I can combine both, 25 29 such as a developer for a video game and/or video game engine. 26 - Here's to the future. {`:-)`} 30 + Here's to the future! 27 31 </p> 28 32 </span> 29 33 <Quote author="Honkai Star Rail">
+3
src/routes/bsky/+page.svelte
··· 1 + <svelte:head> 2 + <meta http-equiv="refresh" content="0; url=https://bsky.app/profile/samanthanguyen.me" /> 3 + </svelte:head>
+3
src/routes/github/+page.svelte
··· 1 + <svelte:head> 2 + <meta http-equiv="refresh" content="0; url=https://github.com/neoncitylights" /> 3 + </svelte:head>
static/fonts/DepartureMono-Regular.woff2

This is a binary file and will not be displayed.

+5 -5
svelte.config.js
··· 1 - import { mdsvex } from "mdsvex"; 2 - import adapter from '@sveltejs/adapter-cloudflare'; 3 - import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 1 + import { mdsvex } from 'mdsvex' 2 + import adapter from '@sveltejs/adapter-cloudflare' 3 + import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' 4 4 5 5 /** @type {import('@sveltejs/kit').Config} */ 6 6 const config = { ··· 11 11 // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. 12 12 // If your environment is not supported, or you settled on a specific environment, switch out the adapter. 13 13 // See https://svelte.dev/docs/kit/adapters for more information about adapters. 14 - adapter: adapter() 14 + adapter: adapter(), 15 15 }, 16 - extensions: [".svelte", ".svx"] 16 + extensions: ['.svelte', '.svx'], 17 17 } 18 18 19 19 export default config
+2 -5
tailwind.config.ts
··· 6 6 extend: { 7 7 fontFamily: { 8 8 sans: ['Public Sans Variable', 'sans-serif'], 9 - domine: ['Domine Variable', 'serif'], 10 - }, 11 - backgroundImage: { 12 - 'separator-light': 'linear-gradient(to right, transparent, #CFB4C5 50%, transparent)', 13 - 'separator-dark': 'linear-gradient(to right, transparent, rgb(39 39 42), transparent)', 9 + serif: ['Domine Variable', 'serif'], 10 + mono: ['Departure Mono', 'monospace'], 14 11 }, 15 12 }, 16 13 },
+2 -2
vite.config.ts
··· 2 2 import { defineConfig } from 'vite' 3 3 4 4 export default defineConfig({ 5 - plugins: [sveltekit()] 6 - }); 5 + plugins: [sveltekit()], 6 + })