feat: skeleton

This commit is contained in:
2025-01-29 17:35:25 -05:00
parent e62e465fec
commit f1ed47c164
21 changed files with 162 additions and 8309 deletions

View File

@@ -1,329 +1,47 @@
@import url('https://fonts.cdnfonts.com/css/jetbrains-mono-2');
@import "tailwind";
:root {
--line-height: 1.2rem;
--border-thickness: 2px;
--text-color: black;
font-optical-sizing: auto;
font-variant-numeric: tabular-nums lining-nums;
}
.dark {
:root {
--text-color: white;
}
}
* {
box-sizing: border-box;
text-decoration-thickness: var(--border-thickness);
}
*::selection {
@apply bg-black text-white dark:bg-white dark:text-black;
}
.button {
border: var(--border-thickness) solid;
padding:
calc(var(--line-height) / 2 - var(--border-thickness)) calc(1ch - var(--border-thickness));
margin: 0;
height: calc(var(--line-height) * 2);
width: auto;
overflow: visible;
line-height: normal;
-webkit-font-smoothing: inherit;
-moz-osx-font-smoothing: inherit;
-webkit-appearance: none;
@apply select-none bg-white dark:bg-black px-1h shadow-box active:shadow-none active:translate-x-[3px] active:translate-y-[3px];
}
.button:focus:not(:active) {
--border-thickness: 3px;
outline: none;
}
hr {
@apply h-2v block relative text-black dark:text-white border-none my-1v;
}
hr:after {
@apply block absolute left-0 h-0 w-full border-black dark:border-white;
content: "";
top: calc(var(--line-height) - var(--border-thickness));
border-top: calc(var(--border-thickness) * 3) double;
}
.jump-text:hover>.jump-text {
animation: jump 0.25s ease-in-out;
animation-delay: var(--animation-delay);
}
@keyframes jump {
0%,
100% {
transform: translateY(0);
}
50% {
transform: translateY(-7px);
}
}
details {
border: var(--border-thickness) solid var(--text-color);
padding: calc(var(--line-height) - var(--border-thickness)) 1ch;
margin-bottom: var(--line-height);
margin-top: var(--line-height);
}
summary {
font-weight: var(--font-weight-medium);
cursor: pointer;
}
details[open] summary {
margin-bottom: var(--line-height);
}
details ::marker {
display: inline-block;
content: '▶';
margin: 0;
}
details[open] ::marker {
content: '▼';
}
details :last-child {
margin-bottom: 0;
}
/* DITHER ANIMATION */
.dither {
background-repeat: repeat;
position: fixed;
inset: 0;
z-index: 50;
image-rendering: optimizeSpeed;
/* STOP SMOOTHING, GIVE ME SPEED */
image-rendering: -moz-crisp-edges;
/* Firefox */
image-rendering: -o-crisp-edges;
/* Opera */
image-rendering: -webkit-optimize-contrast;
/* Chrome (and eventually Safari) */
image-rendering: pixelated;
/* Universal support since 2021 */
image-rendering: optimize-contrast;
/* CSS3 Proposed */
-ms-interpolation-mode: nearest-neighbor;
pointer-events: none;
}
.dark {
.dither {
filter: invert(1)
}
.wave-image {
filter: invert(1)
}
}
.dither-1 {
background-image: url(/images/dither_light_3.png);
}
.dither-2 {
background-image: url(/images/dither_light_3.png);
background-position: 50px 50px;
}
.dither-3 {
background-image: url(/images/dither_light_3.png);
background-position: 100px 100px;
}
@import url("https://fonts.cdnfonts.com/css/jetbrains-mono-2");
@import "tailwindcss";
@config "../tailwind.config.ts";
.tree,
.tree ul {
position: relative;
padding-left: 0;
list-style-type: none;
line-height: var(--line-height);
position: relative;
padding-left: 0;
list-style-type: none;
line-height: var(--line-height);
}
.tree ul {
margin: 0;
margin: 0;
}
.tree ul li {
position: relative;
padding-left: 1.5ch;
margin-left: 1.5ch;
border-left: var(--border-thickness) solid var(--text-color);
position: relative;
padding-left: 1.5ch;
margin-left: 1.5ch;
border-left: var(--border-thickness) solid var(--text-color);
}
.tree ul li:before {
position: absolute;
display: block;
top: calc(var(--line-height) / 2);
left: 0;
content: "";
width: 1ch;
border-bottom: var(--border-thickness) solid var(--text-color);
position: absolute;
display: block;
top: calc(var(--line-height) / 2);
left: 0;
content: "";
width: 1ch;
border-bottom: var(--border-thickness) solid var(--text-color);
}
.tree ul li:last-child {
border-left: none;
border-left: none;
}
.tree ul li:last-child:after {
position: absolute;
display: block;
top: 0;
left: 0;
content: "";
height: calc(var(--line-height) / 2);
border-left: var(--border-thickness) solid var(--text-color);
position: absolute;
display: block;
top: 0;
left: 0;
content: "";
height: calc(var(--line-height) / 2);
border-left: var(--border-thickness) solid var(--text-color);
}
/* DEBUG UTILITIES */
.debug .debug-grid {
--line-height: 1.2rem;
--text-color: #000000;
--background-color: #FFFFFF;
--color: color-mix(in srgb, var(--text-color) 10%, var(--background-color) 90%);
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
background-image:
repeating-linear-gradient(var(--color) 0 1px, transparent 1px 100%),
repeating-linear-gradient(90deg, var(--color) 0 1px, transparent 1px 100%);
background-size: 1ch var(--line-height);
margin: 0;
}
code:not(pre code) {
@apply bg-black text-white dark:bg-white dark:text-black px-1h;
@apply selection:dark:bg-black selection:dark:text-white selection:bg-white selection:text-black;
}
.debug .off-grid {
background: rgba(255, 0, 0, 0.1);
}
.debug-toggle-label {
text-align: right;
}
.debug img {
/* border: 1px solid black; */
}
.formkit-form {
font-family: "JetBrains Mono", monospace;
}
.formkit-form [data-style="clean"] {
@apply !pt-2v !px-0 !pb-1v;
}
.formkit-fields {
@apply !m-0;
}
.formkit-field {
@apply !m-0 !mr-2h;
}
.formkit-input {
border: var(--border-thickness) solid !important;
padding:
calc(var(--line-height) / 2 - var(--border-thickness)) calc(1ch - var(--border-thickness)) !important;
margin: 0 !important;
height: calc(var(--line-height) * 2) !important;
width: 100% !important;
overflow: visible !important;
line-height: normal !important;
-webkit-font-smoothing: inherit !important;
-moz-osx-font-smoothing: inherit !important;
-webkit-appearance: none !important;
@apply !font-medium;
}
.formkit-input:focus:not(:active) {
--border-thickness: 3px;
outline: none;
}
.formkit-submit {
border: var(--border-thickness) solid !important;
padding:
calc(var(--line-height) / 2 - var(--border-thickness)) calc(1ch - var(--border-thickness)) !important;
margin: 0 !important;
height: calc(var(--line-height) * 2) !important;
width: auto !important;
overflow: visible !important;
line-height: normal !important;
-webkit-font-smoothing: inherit !important;
-moz-osx-font-smoothing: inherit !important;
-webkit-appearance: none !important;
@apply !select-none !bg-white !text-black !px-1h !shadow-box !py-0;
}
.formkit-submit:active {
@apply !shadow-none !translate-x-[3px] !translate-y-[3px]
}
.formkit-alert-success {
border-width: 0 !important;
@apply !bg-transparent !text-black !m-0 !p-0 !font-bold;
}
.dark {
.formkit-input {
@apply !border-white !bg-black !text-white;
}
.formkit-submit {
@apply !bg-black !text-white;
}
.formkit-alert-success {
@apply !text-white;
}
}
.formkit-submit span {
padding: 0 !important;
}
.formkit-submit:hover span {
background-color: transparent !important;
}
.formkit-submit:focus:not(:active) {
--border-thickness: 3px !important;
outline: none !important;
}
.pixelated {
image-rendering: pixelated;
}
/* <div id="a6d9b30e24" class="formkit-alert formkit-alert-success" data-element="success" data-group="alert">Success! Check your email to confirm the subscription.</div> */

5
src/app.css.d.ts vendored
View File

@@ -1,5 +0,0 @@
declare const styles: {
readonly "visually-hidden": string;
};
export = styles;

View File

@@ -0,0 +1,33 @@
import { For } from "solid-js";
import JobCard from "./JobCard";
import { createSignal } from "solid-js";
const Experience = () => {
const [jobs, setJobs] = createSignal([
{
title: "Software Developer Engineer Intern",
company: "Amazon (Delivery Extensions Team)",
location: "Toronto, Ontario, Canada",
range: "May 2025 - July 2025",
url: "",
},
{
title: "Software Engineer Intern",
company: "Cisco Systems",
location: "Remote",
range: "January 2023 - May 2023",
url: "https://developer.cisco.com/docs/modeling-labs/cat-9000v/",
},
]);
return (
<section class="mt-6 mx-auto px-4">
<h2 class="mt-6 text-xl font-bold mb-8">Experience</h2>
<div>
<For each={jobs()}>{(job) => <JobCard job={job} />}</For>
</div>
</section>
);
};
export default Experience;

View File

@@ -0,0 +1,17 @@
const JobCard = (props) => {
return (
<div class="mb-8">
<h3 class="font-bold text-xl">
{props.job.title} @
<a href={props.job.url} class="text-blue-500 hover:text-blue-600">
{props.job.company}
</a>
</h3>
<p class="text-gray-600 mb-4">
{props.job.range} | {props.job.location}
</p>
</div>
);
};
export default JobCard;

View File

@@ -1,12 +1,5 @@
import { A } from "@solidjs/router";
import type { ParentComponent } from "solid-js";
import { clientOnly } from "@solidjs/start";
const DarkModeToggle = clientOnly(() =>
import("./DarkModeToggle").then((r) => ({
default: r.DarkModeToggle,
})),
);
function changeFavicon(newFaviconPath: string) {
const link = document.querySelector("link[rel~='icon']") as HTMLLinkElement;
@@ -26,16 +19,14 @@ export const Layout: ParentComponent = (props) => {
<a href="#main-content" class="sr-only">
Skip to main content
</a>
<div class="flex flex-col min-h-screen pt-2v py-1v px-2h max-w-thread mx-auto relative overflow-x-hidden leading-1 box-border decoration-2 underline-offset-2">
<div class="bg-nord-6 flex flex-col min-h-screen pt-2v py-1v px-2h max-w-full mx-auto relative overflow-x-hidden leading-1 box-border decoration-2 underline-offset-2">
<header class="flex flex-col items-center justify-center gap-2v px-4h py-2v">
<a href="/" class="text-2v leading-2 font-bold">
<a href="/" class="text-nord-3 text-2v leading-2 font-bold">
~/minhtrannhat
</a>
<DarkModeToggle />
<nav>
<ul class="flex items-center gap-7h">
<nav class="container mx-auto px-4 py-4">
<ul class="flex flex-wrap justify-center items-center gap-6">
<A end class="hover:underline" activeClass="font-bold" href={"/"}>
Home
</A>
@@ -70,8 +61,6 @@ export const Layout: ParentComponent = (props) => {
<main id="main-content" class="mt-1v flex-auto">
{props.children}
</main>
<div class="debug-grid" />
</div>
</>
);

View File

@@ -1,199 +0,0 @@
/**
* Shades of Purple Theme for Prism.js
*
* @author Ahmad Awais <https://twitter.com/MrAhmadAwais/>
* @support Follow/tweet at https://twitter.com/MrAhmadAwais/
*/
code[class*='language-'],
pre[class*='language-'] {
@apply text-black dark:text-white font-medium;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*='language-']::-moz-selection,
pre[class*='language-'] ::-moz-selection,
code[class*='language-']::-moz-selection,
code[class*='language-'] ::-moz-selection,
pre[class*='language-']::selection,
pre[class*='language-'] ::selection,
code[class*='language-']::selection,
code[class*='language-'] ::selection {
color: inherit;
}
/* Code blocks. */
pre[class*='language-'] {
@apply box-border border-black dark:border-white border-2;
padding: calc(var(--line-height) - var(--border-thickness)) calc(2ch - var(--border-thickness));
overflow: auto;
}
/*
:not(pre)>code[class*='language-'],
pre[class*='language-'] {
}
@media (prefers-color-scheme: dark) {
:not(pre)>code[class*='language-'],
pre[class*='language-'] {
@apply border-white;
}
} */
/* Inline code */
/* :not(pre)>code[class*='language-'] {
} */
.token {
font-weight: 400;
}
.token.comment,
.token.prolog,
.token.cdata {
@apply text-slate-600 dark:text-slate-400;
}
.token.delimiter,
.token.keyword,
.token.selector,
.token.important,
.token.atrule {
@apply text-black dark:text-white;
}
.token.operator,
.token.attr-name {
@apply font-medium text-black dark:text-white;
}
.token.punctuation {
@apply text-slate-500;
}
.token.boolean {
@apply font-medium text-black dark:text-white;
}
.token.tag,
.token.tag .punctuation,
.token.doctype,
.token.builtin {
@apply font-medium text-black dark:text-white;
}
.token.entity,
.token.symbol {
@apply font-medium text-black dark:text-white;
}
.token.number {
@apply font-medium text-black dark:text-white;
}
.token.property,
.token.constant,
.token.variable {
@apply font-medium text-black dark:text-white;
}
.token.string,
.token.char {
@apply text-slate-800 dark:text-slate-200;
font-style: italic;
}
.token.attr-value,
.token.attr-value .punctuation {
color: #a5c261;
}
.token.attr-value .punctuation:first-child {
color: #a9b7c6;
}
.token.url {
color: #287bde;
text-decoration: underline;
}
.token.function {
@apply font-bold text-black dark:text-white;
}
/* .token.regex {
} */
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.inserted {
background: #00ff00;
}
.token.deleted {
background: #ff000d;
}
code.language-css .token.property,
code.language-css .token.property+.token.punctuation {
color: #a9b7c6;
}
code.language-css .token.id {
color: #ffc66d;
}
code.language-css .token.selector>.token.class,
code.language-css .token.selector>.token.attribute,
code.language-css .token.selector>.token.pseudo-class,
code.language-css .token.selector>.token.pseudo-element {
color: #ffc66d;
}
.token.class-name {
@apply font-medium text-slate-700 dark:text-slate-300;
font-style: italic;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
background: none;
}
.line-highlight.line-highlight {
margin-top: 36px;
background: linear-gradient(to right, rgba(179, 98, 255, 0.17), transparent);
}
.line-highlight.line-highlight:before,
.line-highlight.line-highlight[data-end]:after {
content: '';
}

View File

@@ -1,4 +1,3 @@
// @ts-expect-error
import Posts from "./posts.json";
import type { Post } from "~/types";

View File

@@ -9,12 +9,9 @@ export default createHandler(() => (
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<script>
{`document.documentElement.classList.toggle('dark', window.matchMedia('(prefers-color-scheme: dark)').matches)`}
</script>
{assets}
</head>
<body class="font-mono bg-white dark:bg-black dark:text-white">
<body class="font-mono">
<div id="app">{children}</div>
{scripts}
</body>

14
src/jobs/cisco.md Normal file
View File

@@ -0,0 +1,14 @@
---
date: 2022-12-27
title: Software Engineer Intern
company: Cisco Systems
location: Remote
range: January - May 2023
url: https://www.cisco.com/c/en/us/products/cloud-systems-management/modeling-labs/index.html
---
- Developed a **multi-threaded Python microservice** for a Cisco internal SaaS ingesting usage telemetry data of Cisco Cat9kv switches from thousands of GNS3 networking projects to be stored in **Elastic Search**.
- Containerized and integrated **Elastic Search** and **Kibana** into existing microservices system.
- Created insightful **Kibana** charts highlighting Cat9kv switch flavors daily usage and number of daily users to present to Cisco Enterprise Networking Group executives.
- Wrote custom **Ansible** modules to synchronize folders across Cisco managed servers while ensuring 100% folder structure and files integrity with hashing algorithm utilizing Python SHA1 crytographic library.
- Automated custom **Ansible** modules testing with **Bash** scripts. Testing environment made with **Vagrant** and **Docker** to simulate production servers

View File

@@ -1,6 +1,5 @@
import { For } from "solid-js";
import { posts } from "~/data/posts";
import { Posts } from "~/components/Posts";
import Experience from "../components/Experience";
const links = [
"https://github.com/minhtrannhat",
@@ -10,19 +9,21 @@ const links = [
const Homepage = () => {
return (
<div>
<section class="flex flex-col sm:flex-row gap-2v sm:gap-3h">
<section class="mx-4 flex flex-col sm:flex-row gap-2v sm:gap-3h">
<div class="font-medium">
<div class="flex items-end mb-1v gap-1h">
<div class="text-nord-1 flex items-end mb-1v gap-1h">
<p>Hi, Minh here.</p>
</div>
<p class="mb-1v">
I'm Minh Tran, a Computer Engineering student at Concordia
University, Montreal, Canada.
<p class="mb-1v text-nord-1">
I'm a Computer Engineering student at Concordia University,
Montreal, Canada.
<br />
<br />
I'm most passionate about designing distributed systems that scales
but I'm also interested in compilers and systems programming. When
I'm not coding, I read books, listen to podcasts or study music
but I'm also interested in compilers and systems programming.
<br />
<br />
When I'm not coding, I read books, listen to podcasts or study music
theory.
</p>
<p>
@@ -37,10 +38,10 @@ const Homepage = () => {
</a>
</p>
</div>
<ul class="sm:mt-3v text-slate-600 dark:text-slate-200 text-base sm:text-sm leading-1">
<ul class="mx-4 sm:mx-6 sm:mt-3v text-slate-600 text-base sm:text-sm leading-1">
<For each={links}>
{(link) => (
<li class="list-square hover:text-black dark:hover:text-white ml-2h leading-1">
<li class="list-square hover:text-black ml-2h leading-1">
<a
target="_blank"
rel="noreferrer"
@@ -55,7 +56,8 @@ const Homepage = () => {
</ul>
</section>
<hr />
<br />
<Experience />
</div>
);
};

View File

@@ -5,7 +5,6 @@ import { posts } from "~/data/posts";
import { MDXProvider } from "solid-mdx";
import { markdownComponents, PostImage } from "~/components/Markdown";
import dayjs from "dayjs";
import "../css/prism-theme.css";
import type { Post } from "~/types";
const Blog = (props: RouteSectionProps<unknown>) => {

View File

@@ -1,73 +0,0 @@
import type { Accessor } from "solid-js";
import { isServer } from "solid-js/web";
import { useRouteTransitionTiming } from "./useRouteTransitionTiming";
export const useDitherAnimation = (ref: Accessor<HTMLElement | undefined>) => {
if (!isServer) {
const d1 = document.createElement("div");
d1.classList.add("dither", "dither-1");
const d2 = document.createElement("div");
d2.classList.add("dither", "dither-2");
const d3 = document.createElement("div");
d3.classList.add("dither", "dither-3");
let started = false;
useRouteTransitionTiming(
300,
() => {
ref()?.appendChild(d1);
setTimeout(() => {
ref()?.appendChild(d2);
}, 100);
setTimeout(() => {
ref()?.appendChild(d3);
}, 200);
started = true;
},
() => {
const rnd = () =>
setTimeout(() => {
try {
d1.style.backgroundPosition = `${Math.round(
Math.random() * 100,
)}px ${Math.round(Math.random() * 100)}px`;
d2.style.backgroundPosition = `${Math.round(
Math.random() * 100,
)}px ${Math.round(Math.random() * 100)}px`;
d3.style.backgroundPosition = `${Math.round(
Math.random() * 100,
)}px ${Math.round(Math.random() * 100)}px`;
} catch (error) {
console.error(error);
}
if (started) {
rnd();
}
}, 100);
rnd();
},
() => {
started = false;
try {
ref()?.removeChild(d3);
} catch (error) {
console.error(error);
}
setTimeout(() => {
try {
ref()?.removeChild(d2);
} catch (error) {
console.error(error);
}
}, 100);
setTimeout(() => {
try {
ref()?.removeChild(d1);
} catch (error) {
console.error(error);
}
}, 200);
},
);
}
};

View File

@@ -1,24 +0,0 @@
import { useIsRouting, useBeforeLeave } from "@solidjs/router";
import { createEffect } from "solid-js";
export const useRouteTransitionTiming = (
transitionTime: number,
onEnter: () => void,
onLoading: () => void,
onExit: () => void,
) => {
const isRouting = useIsRouting();
createEffect((oldR: boolean | undefined) => {
const r = isRouting();
if (oldR && !r) onExit();
return r;
});
useBeforeLeave((e) => {
e.preventDefault();
onEnter();
setTimeout(() => {
e.retry(true);
onLoading();
}, transitionTime);
});
};