hat
This commit is contained in:
@@ -19,7 +19,7 @@ body {
|
||||
* {
|
||||
color: var(--color-text);
|
||||
font-family: 'Hurmit';
|
||||
cursor: text;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.pixelated {
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
<script lang="js" setup>
|
||||
import HeaderLinks from './HeaderLinks.vue';
|
||||
import SiteOptions from './site_options/SiteOptions.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tui-minimalbar">
|
||||
<div class="minimal-inner">
|
||||
<div class="left">
|
||||
<span class="sticky-title" aria-hidden="true">◆ ARANROIG.COM</span>
|
||||
<HeaderLinks />
|
||||
</div>
|
||||
<SiteOptions />
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 80px"></div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tui-minimalbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
|
||||
background: var(--color-sticky-header-bg);
|
||||
backdrop-filter: blur(8px);
|
||||
box-shadow: 0 4px 0px 0px var(--color-container-shadow);
|
||||
}
|
||||
|
||||
.minimal-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 6px 24px;
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
padding: 5px 16px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
padding: 4px 12px;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.sticky-title {
|
||||
font-family: 'Hurmit', monospace;
|
||||
font-size: 0.8rem;
|
||||
color: var(--color-text);
|
||||
letter-spacing: 1px;
|
||||
white-space: nowrap;
|
||||
|
||||
@media screen and (max-width: 900px) {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,107 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import HeaderLinks from './HeaderLinks.vue';
|
||||
import SiteOptions from './site_options/SiteOptions.vue';
|
||||
import StickyHeader from './StickyHeader.vue';
|
||||
|
||||
const asciiLines = [
|
||||
"░█▀█░█▀▄░█▀█░█▀█░█▀▄░█▀█░▀█▀░█▀▀",
|
||||
"░█▀█░█▀▄░█▀█░█░█░█▀▄░█░█░░█░░█░█",
|
||||
"░▀░▀░▀░▀░▀░▀░▀░▀░▀░▀░▀▀▀░▀▀▀░▀▀▀"
|
||||
];
|
||||
|
||||
const revealedLines = ref<number>(0);
|
||||
let asciiTimer: ReturnType<typeof setInterval> | null = null;
|
||||
const HAS_ANIMATED_KEY = 'ascii-animated';
|
||||
|
||||
function startAsciiAnimation() {
|
||||
if (sessionStorage.getItem(HAS_ANIMATED_KEY)) {
|
||||
revealedLines.value = asciiLines.length;
|
||||
return;
|
||||
}
|
||||
|
||||
const delay = 400;
|
||||
let count = 0;
|
||||
|
||||
asciiTimer = setInterval(() => {
|
||||
count++;
|
||||
revealedLines.value = count;
|
||||
if (count >= asciiLines.length) {
|
||||
clearInterval(asciiTimer!);
|
||||
asciiTimer = null;
|
||||
sessionStorage.setItem(HAS_ANIMATED_KEY, '1');
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
startAsciiAnimation();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (asciiTimer) clearInterval(asciiTimer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="header">
|
||||
<div class="container">
|
||||
<div class="header-container website">
|
||||
<pre v-if="revealedLines > 0" class="ascii-title" aria-hidden="true">{{ asciiLines.slice(0, revealedLines).join('\n') }}</pre>
|
||||
<HeaderLinks></HeaderLinks>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-container">
|
||||
</div>
|
||||
<div class="container">
|
||||
<SiteOptions></SiteOptions>
|
||||
</div>
|
||||
</div>
|
||||
<StickyHeader></StickyHeader>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header {
|
||||
position: relative;
|
||||
margin-top: 30px;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
&.website {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-container {
|
||||
position:relative;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.ascii-title {
|
||||
font-family: 'Hurmit', monospace;
|
||||
color: var(--color-link);
|
||||
text-shadow: 0 0 8px var(--color-link), 0 0 4px var(--color-link);
|
||||
font-size: clamp(0.35rem, 1.2vw, 0.65rem);
|
||||
line-height: 1;
|
||||
letter-spacing: -0.1ch;
|
||||
margin: 0;
|
||||
white-space: pre;
|
||||
min-height: clamp(1.05rem, 3.6vw, 1.95rem);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 600px) {
|
||||
.ascii-title {
|
||||
font-size: clamp(0.28rem, 1.8vw, 0.5rem);
|
||||
line-height: 1;
|
||||
letter-spacing: -0.1ch;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup>
|
||||
import MinimalHeader from '~/components/parts/MinimalHeader.vue';
|
||||
import PageHeader from '~/components/parts/PageHeader.vue';
|
||||
import TableHeader from '~/components/parts/TableHeader.vue';
|
||||
|
||||
const slug = useRoute().params.slug;
|
||||
const { locale } = useI18n();
|
||||
@@ -12,7 +11,9 @@ const { data: post } = await useAsyncData(`art-${slug}`, () =>
|
||||
|
||||
<template>
|
||||
<!-- Render the blog post as Prose & Vue components -->
|
||||
<MinimalHeader></MinimalHeader>
|
||||
<div class="no-sprite">
|
||||
<TableHeader></TableHeader>
|
||||
</div>
|
||||
<div class="extended-container">
|
||||
<ContentRenderer v-if="post" :value="post" class="art" />
|
||||
</div>
|
||||
@@ -39,4 +40,10 @@ const { data: post } = await useAsyncData(`art-${slug}`, () =>
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.no-sprite .undertable-wrapper {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue';
|
||||
import FixedLayout from '~/components/layouts/FixedLayout.vue';
|
||||
import MinimalHeader from '~/components/parts/MinimalHeader.vue';
|
||||
import TableHeader from '~/components/parts/TableHeader.vue';
|
||||
|
||||
const { locale, t } = useI18n();
|
||||
const localePath = useLocalePath();
|
||||
@@ -53,7 +53,9 @@ const displayedArt = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MinimalHeader></MinimalHeader>
|
||||
<div class="no-sprite">
|
||||
<TableHeader></TableHeader>
|
||||
</div>
|
||||
<FixedLayout>
|
||||
<Container>
|
||||
<h2 class="section-title">ART GALLERY</h2>
|
||||
@@ -235,3 +237,9 @@ const displayedArt = computed(() => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.no-sprite .undertable-wrapper {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import FixedLayout from '~/components/layouts/FixedLayout.vue';
|
||||
import PageHeader from '~/components/parts/PageHeader.vue';
|
||||
import TableHeader from '~/components/parts/TableHeader.vue';
|
||||
|
||||
const slug = useRoute().params.slug;
|
||||
const { locale } = useI18n();
|
||||
@@ -12,7 +12,9 @@ const { data: post } = await useAsyncData(`blog-${slug}`, () =>
|
||||
|
||||
<template>
|
||||
<!-- Render the blog post as Prose & Vue components -->
|
||||
<PageHeader></PageHeader>
|
||||
<div class="no-sprite">
|
||||
<TableHeader></TableHeader>
|
||||
</div>
|
||||
<FixedLayout>
|
||||
<Container>
|
||||
<ContentRenderer v-if="post" :value="post" class="blog" />
|
||||
@@ -36,4 +38,10 @@ const { data: post } = await useAsyncData(`blog-${slug}`, () =>
|
||||
max-height: 400px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.no-sprite .undertable-wrapper {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import MinimalHeader from '~/components/parts/MinimalHeader.vue';
|
||||
import { useAsyncData } from '#app';
|
||||
import TableHeader from '~/components/parts/TableHeader.vue';
|
||||
import FixedLayout from '~/components/layouts/FixedLayout.vue';
|
||||
import { ref, computed } from 'vue';
|
||||
const { locale } = useI18n();
|
||||
@@ -26,7 +25,9 @@ const displayedPosts = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<MinimalHeader></MinimalHeader>
|
||||
<div class="no-sprite">
|
||||
<TableHeader></TableHeader>
|
||||
</div>
|
||||
<FixedLayout>
|
||||
<Container>
|
||||
<section class="blog-section">
|
||||
@@ -165,3 +166,9 @@ const displayedPosts = computed(() => {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.no-sprite .undertable-wrapper {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,7 +13,9 @@ const { data: markdown } = await useAsyncData(`fixed`, async () =>
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TableHeader></TableHeader>
|
||||
<div class="no-sprite">
|
||||
<TableHeader></TableHeader>
|
||||
</div>
|
||||
|
||||
<FixedLayout>
|
||||
<Container>
|
||||
@@ -36,4 +38,10 @@ p {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.no-sprite .undertable-wrapper {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
@@ -29,6 +29,19 @@ const { data: contactMarkdown } = await useAsyncData(`fixed-contact`, async () =
|
||||
|
||||
const localePath = useLocalePath();
|
||||
|
||||
function resolveProjectLink(project) {
|
||||
if (project.link) return project.link;
|
||||
const slugToProjectMap = {
|
||||
'dragonroll': 'https://dragonroll.aranroig.com',
|
||||
};
|
||||
return slugToProjectMap[project.slug] || null;
|
||||
}
|
||||
|
||||
function navigateToProject(project) {
|
||||
const link = resolveProjectLink(project);
|
||||
if (link) window.open(link, '_blank', 'noopener,noreferrer');
|
||||
}
|
||||
|
||||
const artPosts = useState<any[]>('art-posts', () => null as any);
|
||||
if (!artPosts.value?.length) {
|
||||
const currentLocale = locale.value;
|
||||
@@ -125,33 +138,37 @@ const sectionTargets = {
|
||||
<section class="projects-section" id="scroll-projects" v-if="projects && projects.length > 0">
|
||||
<Container>
|
||||
<h2 class="section-title">{{ t('pages.projects_heading') }}</h2>
|
||||
<!--
|
||||
|
||||
<div class="projects-grid">
|
||||
<div
|
||||
v-for="project in projects"
|
||||
v-for="project in projects"
|
||||
|
||||
:key="project.slug"
|
||||
class="project-card"
|
||||
class="project-card-wrapper"
|
||||
>
|
||||
<span class="project-card-corner tl"></span>
|
||||
<span class="project-card-corner tr"></span>
|
||||
<span class="project-card-corner bl"></span>
|
||||
<span class="project-card-corner br"></span>
|
||||
|
||||
<a
|
||||
:href="project.link"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="project-title"
|
||||
>{{ project.title }}</a>
|
||||
<p class="project-description">{{ project.description }}</p>
|
||||
<div class="project-tech">
|
||||
<span v-for="tech in project.tech" :key="tech" class="tech-tag">{{ tech }}</span>
|
||||
<div
|
||||
class="project-card"
|
||||
@click="navigateToProject(project)"
|
||||
>
|
||||
<div class="project-layout">
|
||||
<img
|
||||
v-if="project.preview"
|
||||
:src="project.preview"
|
||||
:alt="`Preview of ${project.title}`"
|
||||
class="project-preview"
|
||||
/>
|
||||
<div class="project-info">
|
||||
<p class="project-title">{{ project.title }}</p>
|
||||
<p class="project-description">{{ project.description }}</p>
|
||||
<div class="project-tech">
|
||||
<span v-for="tech in project.tech" :key="tech" class="tech-tag">{{ tech }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
-->
|
||||
(Under construction...)
|
||||
</Container>
|
||||
</section>
|
||||
|
||||
@@ -388,11 +405,6 @@ const sectionTargets = {
|
||||
padding: 12px 16px;
|
||||
transition: all 0.1s steps(2, end);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-link);
|
||||
box-shadow: 4px -4px 0px 0px var(--color-link);
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "[ PROJECT ]";
|
||||
position: absolute;
|
||||
@@ -404,6 +416,28 @@ const sectionTargets = {
|
||||
color: var(--color-link);
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.project-layout {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.project-preview {
|
||||
width: 140px;
|
||||
min-width: 140px;
|
||||
height: 85px;
|
||||
object-fit: cover;
|
||||
border: 1px solid var(--color-border-color);
|
||||
image-rendering: auto;
|
||||
transition: border-color 0.1s steps(2, end);
|
||||
}
|
||||
|
||||
.project-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.project-card-corner {
|
||||
@@ -420,18 +454,29 @@ const sectionTargets = {
|
||||
|
||||
.project-title {
|
||||
font-family: 'Hurmit', monospace;
|
||||
color: var(--color-link);
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-size: 1.05rem;
|
||||
text-shadow: 0 0 4px var(--color-link);
|
||||
display: block;
|
||||
text-shadow: inherit;
|
||||
|
||||
&:hover {
|
||||
color: var(--color-link);
|
||||
text-shadow: 0 0 12px var(--color-link), 0 0 4px var(--color-link);
|
||||
}
|
||||
}
|
||||
|
||||
.project-card {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.project-card-wrapper:hover .project-card {
|
||||
border-color: var(--color-link);
|
||||
box-shadow: 4px -4px 0px 0px var(--color-link);
|
||||
}
|
||||
|
||||
.project-info {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.project-description {
|
||||
font-family: 'Hurmit', monospace;
|
||||
color: var(--color-text);
|
||||
@@ -762,6 +807,16 @@ const sectionTargets = {
|
||||
.project-card {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.project-layout {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
.project-preview {
|
||||
width: 100% !important;
|
||||
min-width: auto !important;
|
||||
height: 160px !important;
|
||||
}
|
||||
|
||||
.project-title {
|
||||
font-size: 0.95rem;
|
||||
|
||||
Reference in New Issue
Block a user