Improved UI
@@ -9,7 +9,7 @@ Pure static front-end site served by nginx. No build step required.
|
|||||||
|
|
||||||
```
|
```
|
||||||
public/ Site root (served by nginx)
|
public/ Site root (served by nginx)
|
||||||
├── index.html Single page: home, about, apps, games
|
├── index.html Single page: home, about, apps, hosted, games
|
||||||
├── favicon.ico
|
├── favicon.ico
|
||||||
└── assets/
|
└── assets/
|
||||||
├── css/ douwco.css (branding), style.css (layout)
|
├── css/ douwco.css (branding), style.css (layout)
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ footer > div > p {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
color: var(--white-clr);
|
color: var(--white-clr);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-size: 36px;
|
font-size: 28px;
|
||||||
|
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
@@ -100,7 +100,7 @@ footer > div > p {
|
|||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 10px;
|
margin: 8px;
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,328 +202,156 @@ footer > div > p {
|
|||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* New project card styling - rectangular with image on top */
|
|
||||||
.project-card-new {
|
|
||||||
background-color: var(--background-accent-clr);
|
|
||||||
border-radius: var(--card-border-radius, 25px);
|
|
||||||
overflow: hidden;
|
|
||||||
margin: 10px;
|
|
||||||
max-width: 400px;
|
|
||||||
min-width: 300px;
|
|
||||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
cursor: pointer;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-new.expanded {
|
|
||||||
z-index: 10;
|
|
||||||
transform: scale(1.02);
|
|
||||||
box-shadow: 0 12px 35px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-new .toggle-icon {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: var(--green-clr);
|
|
||||||
color: var(--background-clr);
|
|
||||||
font-size: 20px;
|
|
||||||
font-weight: bold;
|
|
||||||
z-index: 20;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: transform 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-new .toggle-icon:hover {
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-image-container {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 200px;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-new.expanded .project-card-image-container {
|
|
||||||
height: 150px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
object-fit: cover;
|
|
||||||
background-color: #888; /* Plain grey as fallback */
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-title-container {
|
|
||||||
position: relative;
|
|
||||||
margin-top: -50px; /* Overlap with image */
|
|
||||||
padding: 10px 20px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-title-background {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: var(--background-accent-clr);
|
|
||||||
border-radius: var(--card-border-radius, 25px);
|
|
||||||
opacity: 0.95;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-title {
|
|
||||||
position: relative;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 20px;
|
|
||||||
color: var(--green-clr);
|
|
||||||
margin: 0;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-content {
|
|
||||||
padding: 0 20px;
|
|
||||||
color: var(--text-color);
|
|
||||||
max-height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), padding-top 0.4s cubic-bezier(0.4, 0, 0.2, 1), padding-bottom 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-new.expanded .project-card-content {
|
|
||||||
max-height: 1000px;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-short-desc {
|
|
||||||
padding: 10px 20px 20px;
|
|
||||||
color: var(--text-color);
|
|
||||||
line-height: 1.6;
|
|
||||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-new.expanded .project-card-short-desc {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.project-card-content p {
|
|
||||||
margin: 0;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* About Me grid layout */
|
/* About Me grid layout */
|
||||||
.about-me-projects-grid {
|
.about-me-projects-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 20px auto;
|
margin: 20px auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ===== Apps Section - Full Width Dashboard Carousel ===== */
|
/* ===== Apps & Hosted Sections - vertical selector + detail ===== */
|
||||||
|
|
||||||
.apps-container {
|
.service-section {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 1600px;
|
max-width: 1600px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 20px;
|
padding: 0 20px 20px;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-container-full {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
padding: 40px 60px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-track-full {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
gap: 30px;
|
||||||
align-items: center;
|
align-items: stretch;
|
||||||
gap: 20px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.carousel-item-full {
|
.service-selector {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
width: 260px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
max-height: 480px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-selector-item {
|
||||||
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: 18px;
|
||||||
|
background-color: var(--background-accent-clr);
|
||||||
|
color: var(--white-clr);
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
z-index: 1;
|
text-align: left;
|
||||||
min-width: 120px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.carousel-item-full[data-selected="true"] {
|
.service-selector-item:hover {
|
||||||
z-index: 5;
|
transform: translateY(-2px);
|
||||||
|
background-color: #3a5c5c;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-icon-large {
|
.service-selector-item.selected {
|
||||||
width: 100px;
|
border-color: var(--green-clr);
|
||||||
height: 100px;
|
background-color: var(--dark-teal, #2c5f5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-selector-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
flex-shrink: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: var(--icon-bg, #888);
|
background-color: var(--icon-bg, #888);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: var(--white-clr);
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 40px;
|
|
||||||
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||||
}
|
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
|
||||||
.carousel-item-full[data-selected="true"] .service-icon-large {
|
|
||||||
width: 150px;
|
|
||||||
height: 150px;
|
|
||||||
font-size: 60px;
|
|
||||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-item-full:nth-child(1):not([data-selected="true"]) .service-icon-large,
|
|
||||||
.carousel-item-full:nth-child(5):not([data-selected="true"]) .service-icon-large {
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
font-size: 32px;
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-item-full:nth-child(2):not([data-selected="true"]):not(:nth-child(4)) .service-icon-large,
|
|
||||||
.carousel-item-full:nth-child(4):not([data-selected="true"]):not(:nth-child(2)) .service-icon-large {
|
|
||||||
width: 90px;
|
|
||||||
height: 90px;
|
|
||||||
font-size: 36px;
|
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-label-large {
|
|
||||||
color: var(--white-clr);
|
|
||||||
font-size: 22px;
|
|
||||||
margin-top: 15px;
|
|
||||||
opacity: 0.8;
|
|
||||||
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-item-full[data-selected="true"] .service-label-large {
|
|
||||||
opacity: 1;
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-item-full:nth-child(1):not([data-selected="true"]) .service-label-large,
|
|
||||||
.carousel-item-full:nth-child(5):not([data-selected="true"]) .service-label-large {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-nav-full {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
background: rgba(255, 255, 255, 0.15);
|
|
||||||
border: none;
|
|
||||||
color: var(--white-clr);
|
|
||||||
font-size: 48px;
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
border-radius: 50%;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-nav-full:hover {
|
|
||||||
background: rgba(255, 255, 255, 0.25);
|
|
||||||
transform: translateY(-50%) scale(1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-nav-full.prev {
|
|
||||||
left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-nav-full.next {
|
|
||||||
right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Service detail area - full width */
|
|
||||||
.service-detail-full {
|
|
||||||
width: 100%;
|
|
||||||
background-color: var(--dark-green, #1a3d2e);
|
|
||||||
border-radius: var(--card-border-radius, 25px);
|
|
||||||
padding: 40px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-screenshot-full {
|
|
||||||
width: 100%;
|
|
||||||
height: 400px;
|
|
||||||
border-radius: 15px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: #2a2a2a;
|
|
||||||
transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-screenshot-full img {
|
.service-selector-icon img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: contain;
|
||||||
opacity: 0;
|
padding: 5px;
|
||||||
transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-screenshot-full img.active {
|
.service-selector-item.selected .service-selector-icon {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-selector-label {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-detail {
|
||||||
|
flex: 1;
|
||||||
|
background-color: var(--dark-green, #1a3d2e);
|
||||||
|
border-radius: var(--card-border-radius, 25px);
|
||||||
|
padding: 30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
gap: 30px;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-visual {
|
||||||
|
flex: 1 1 45%;
|
||||||
|
height: clamp(240px, 45vh, 420px);
|
||||||
|
border-radius: 15px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: linear-gradient(145deg, #2c5f5f, #1a3d2e);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-visual-img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
padding: 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-info-full {
|
.service-visual-img.fading {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-info {
|
||||||
|
flex: 1 1 55%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
gap: 20px;
|
gap: 16px;
|
||||||
text-align: center;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-title {
|
.service-title {
|
||||||
color: var(--green-clr);
|
color: var(--green-clr);
|
||||||
font-size: 42px;
|
font-size: 42px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1) 0.1s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-title.active {
|
.service-description {
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-description-full {
|
|
||||||
color: var(--white-clr);
|
color: var(--white-clr);
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
max-width: 800px;
|
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1) 0.2s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-description-full.active {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-description-full p {
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,9 +369,7 @@ footer > div > p {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
||||||
margin-top: 20px;
|
margin-top: 10px;
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.6s cubic-bezier(0.4, 0, 0.2, 1) 0.3s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.go-to-app-button:hover {
|
.go-to-app-button:hover {
|
||||||
@@ -551,10 +377,6 @@ footer > div > p {
|
|||||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.go-to-app-button.active {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===== Games Section ===== */
|
/* ===== Games Section ===== */
|
||||||
|
|
||||||
.games-container {
|
.games-container {
|
||||||
@@ -566,7 +388,7 @@ footer > div > p {
|
|||||||
|
|
||||||
.games-grid {
|
.games-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
gap: 30px;
|
gap: 30px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@@ -587,18 +409,30 @@ footer > div > p {
|
|||||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.4);
|
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-card.expanded {
|
||||||
|
grid-column: span 2;
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0 12px 35px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
.game-card-image {
|
.game-card-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 200px;
|
height: 200px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: #888;
|
background: linear-gradient(145deg, #2c5f5f, #1a3d2e);
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-card-image img {
|
.game-card-image img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
background-color: #888;
|
}
|
||||||
|
|
||||||
|
/* Logo-style images (About Me cards): show the full logo instead of cropping */
|
||||||
|
.game-card-image.contain img {
|
||||||
|
object-fit: contain;
|
||||||
|
padding: 24px;
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-card-content {
|
.game-card-content {
|
||||||
@@ -606,6 +440,7 @@ footer > div > p {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.game-card-title {
|
.game-card-title {
|
||||||
@@ -615,6 +450,20 @@ footer > div > p {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-card-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-genre {
|
||||||
|
color: var(--white-clr);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
.game-card-description {
|
.game-card-description {
|
||||||
color: var(--white-clr);
|
color: var(--white-clr);
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
@@ -680,77 +529,106 @@ footer > div > p {
|
|||||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-card-content .play-button {
|
||||||
|
align-self: flex-start;
|
||||||
|
padding: 10px 24px;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.play-button:hover {
|
.play-button:hover {
|
||||||
transform: scale(1.05);
|
transform: scale(1.05);
|
||||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
|
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive design for carousel */
|
/* Responsive design for the service sections */
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
.apps-container {
|
.service-selector {
|
||||||
padding: 15px;
|
width: 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.carousel-container-full {
|
.service-selector-item {
|
||||||
padding: 30px 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-track-full {
|
|
||||||
gap: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-icon-large {
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
font-size: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-item-full[data-selected="true"] .service-icon-large {
|
|
||||||
width: 120px;
|
|
||||||
height: 120px;
|
|
||||||
font-size: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-item-full:nth-child(1):not([data-selected="true"]) .service-icon-large,
|
|
||||||
.carousel-item-full:nth-child(5):not([data-selected="true"]) .service-icon-large {
|
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.carousel-item-full:nth-child(2):not([data-selected="true"]):not(:nth-child(4)) .service-icon-large,
|
|
||||||
.carousel-item-full:nth-child(4):not([data-selected="true"]):not(:nth-child(2)) .service-icon-large {
|
|
||||||
width: 70px;
|
|
||||||
height: 70px;
|
|
||||||
font-size: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-screenshot-full {
|
|
||||||
height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-title {
|
|
||||||
font-size: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-description-full {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.service-label-large {
|
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.carousel-item-full[data-selected="true"] .service-label-large {
|
.service-detail {
|
||||||
font-size: 24px;
|
padding: 20px;
|
||||||
|
gap: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.service-visual {
|
||||||
|
height: clamp(220px, 40vh, 340px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-title {
|
||||||
|
font-size: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-description {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.go-to-app-button {
|
.go-to-app-button {
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
padding: 15px 35px;
|
padding: 15px 35px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.service-section {
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0 10px 20px;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-selector {
|
||||||
|
flex-direction: row;
|
||||||
|
width: 100%;
|
||||||
|
max-height: none;
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: hidden;
|
||||||
|
padding: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-selector-item {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 10px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-selector-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-detail {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-visual {
|
||||||
|
width: 100%;
|
||||||
|
height: clamp(200px, 35vh, 320px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.service-info {
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Below this width the card grids have a single column: expanding tiles
|
||||||
|
take the full width instead of spanning two columns. */
|
||||||
|
@media (max-width: 740px) {
|
||||||
|
.game-card.expanded {
|
||||||
|
grid-column: span 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-card-full-description {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Currently working on and However in the past sections */
|
/* Currently working on and However in the past sections */
|
||||||
.about-me-section {
|
.about-me-section {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -778,11 +656,11 @@ footer > div > p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.icon.small {
|
.icon.small {
|
||||||
height: 45px;
|
height: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon.medium {
|
.icon.medium {
|
||||||
height: 100px;
|
height: 70px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon.large {
|
.icon.large {
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 630 500" width="630" height="500">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="fg" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#12222a"/>
|
||||||
|
<stop offset="1" stop-color="#0d1117"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="line" x1="0" y1="0" x2="1" y2="0">
|
||||||
|
<stop offset="0" stop-color="#3fb950"/>
|
||||||
|
<stop offset="1" stop-color="#58a6ff"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="630" height="500" fill="url(#fg)"/>
|
||||||
|
|
||||||
|
<!-- top bar -->
|
||||||
|
<rect x="30" y="28" width="570" height="54" rx="10" fill="#161b22" stroke="#30363d" stroke-width="2"/>
|
||||||
|
<rect x="48" y="48" width="120" height="14" rx="7" fill="#3fb950"/>
|
||||||
|
<rect x="470" y="44" width="112" height="22" rx="11" fill="#21262d" stroke="#30363d" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- balance card -->
|
||||||
|
<rect x="30" y="100" width="270" height="120" rx="10" fill="#161b22" stroke="#30363d" stroke-width="2"/>
|
||||||
|
<rect x="48" y="118" width="90" height="10" rx="5" fill="#8b949e"/>
|
||||||
|
<text x="48" y="168" font-family="monospace" font-size="34" font-weight="700" fill="#e6edf3">€ 1 284,50</text>
|
||||||
|
<rect x="48" y="186" width="70" height="9" rx="4" fill="#3fb950"/>
|
||||||
|
<rect x="126" y="186" width="52" height="9" rx="4" fill="#21262d"/>
|
||||||
|
|
||||||
|
<!-- spending chart card -->
|
||||||
|
<rect x="330" y="100" width="270" height="120" rx="10" fill="#161b22" stroke="#30363d" stroke-width="2"/>
|
||||||
|
<rect x="348" y="118" width="80" height="10" rx="5" fill="#8b949e"/>
|
||||||
|
<g fill="#3fb950">
|
||||||
|
<rect x="352" y="168" width="18" height="28" rx="4"/>
|
||||||
|
<rect x="382" y="152" width="18" height="44" rx="4"/>
|
||||||
|
<rect x="412" y="160" width="18" height="36" rx="4"/>
|
||||||
|
<rect x="442" y="134" width="18" height="62" rx="4"/>
|
||||||
|
<rect x="472" y="146" width="18" height="50" rx="4"/>
|
||||||
|
</g>
|
||||||
|
<g fill="#58a6ff">
|
||||||
|
<rect x="510" y="156" width="18" height="40" rx="4"/>
|
||||||
|
<rect x="540" y="122" width="18" height="74" rx="4"/>
|
||||||
|
<rect x="570" y="142" width="18" height="54" rx="4"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- line chart card -->
|
||||||
|
<rect x="30" y="240" width="570" height="150" rx="10" fill="#161b22" stroke="#30363d" stroke-width="2"/>
|
||||||
|
<rect x="48" y="258" width="110" height="10" rx="5" fill="#8b949e"/>
|
||||||
|
<polyline points="48,370 108,344 168,356 228,312 288,326 348,290 408,300 468,268 528,280 582,252"
|
||||||
|
fill="none" stroke="url(#line)" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<circle cx="582" cy="252" r="7" fill="#3fb950" stroke="#0d1117" stroke-width="3"/>
|
||||||
|
|
||||||
|
<!-- category rows -->
|
||||||
|
<rect x="30" y="410" width="570" height="62" rx="10" fill="#161b22" stroke="#30363d" stroke-width="2"/>
|
||||||
|
<circle cx="58" cy="441" r="12" fill="#3fb950"/>
|
||||||
|
<rect x="82" y="433" width="96" height="14" rx="7" fill="#8b949e"/>
|
||||||
|
<circle cx="238" cy="441" r="12" fill="#58a6ff"/>
|
||||||
|
<rect x="262" y="433" width="80" height="14" rx="7" fill="#8b949e"/>
|
||||||
|
<circle cx="402" cy="441" r="12" fill="#d29922"/>
|
||||||
|
<rect x="426" y="433" width="88" height="14" rx="7" fill="#8b949e"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 296 KiB |
|
Before Width: | Height: | Size: 5.6 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 172 KiB |
@@ -0,0 +1,16 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 272" width="192" height="272">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="g" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#78e0a1"/>
|
||||||
|
<stop offset="1" stop-color="#47bcdf"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g fill="none" stroke="url(#g)" stroke-width="14" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="20" y="30" width="152" height="90" rx="18"/>
|
||||||
|
<rect x="20" y="152" width="152" height="90" rx="18"/>
|
||||||
|
<circle cx="52" cy="75" r="7" fill="url(#g)" stroke="none"/>
|
||||||
|
<circle cx="52" cy="197" r="7" fill="url(#g)" stroke="none"/>
|
||||||
|
<path d="M92 75h48"/>
|
||||||
|
<path d="M92 197h48"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 744 B |
@@ -2,9 +2,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
setupNavigationHighlight();
|
setupNavigationHighlight();
|
||||||
setupNavigationClicks();
|
setupNavigationClicks();
|
||||||
setupCopyright();
|
setupCopyright();
|
||||||
setupProjectCards();
|
setupServiceSections();
|
||||||
setupAppsCarousel();
|
setupCards();
|
||||||
setupGameCards();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function setupNavigationHighlight() {
|
function setupNavigationHighlight() {
|
||||||
@@ -47,212 +46,32 @@ function setupCopyright() {
|
|||||||
document.getElementById('copyright').textContent = `© ${new Date().getFullYear()} - douwco.be`;
|
document.getElementById('copyright').textContent = `© ${new Date().getFullYear()} - douwco.be`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupProjectCards() {
|
/* Unified expandable card system, shared by the About Me and My Games grids.
|
||||||
const projectCards = document.querySelectorAll('.project-card-new');
|
Clicking a card expands it (spanning two grid columns); clicking again or
|
||||||
|
picking another card in the same grid collapses it. */
|
||||||
projectCards.forEach(card => {
|
function setupCards() {
|
||||||
const shortDesc = card.querySelector('.project-card-short-desc');
|
const cards = document.querySelectorAll('.game-card');
|
||||||
const fullContent = card.querySelector('.project-card-content');
|
|
||||||
const toggleIcon = card.querySelector('.toggle-icon');
|
cards.forEach(card => {
|
||||||
|
card.addEventListener('click', (event) => {
|
||||||
if (!shortDesc || !fullContent || !toggleIcon) return;
|
if (event.target.closest('a')) {
|
||||||
|
|
||||||
const toggleCard = (event) => {
|
|
||||||
if (event.target.tagName === 'A') {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isExpanded = card.classList.contains('expanded');
|
|
||||||
|
|
||||||
if (isExpanded) {
|
|
||||||
card.classList.remove('expanded');
|
|
||||||
toggleIcon.textContent = '+';
|
|
||||||
} else {
|
|
||||||
card.classList.add('expanded');
|
|
||||||
toggleIcon.textContent = '−';
|
|
||||||
}
|
|
||||||
|
|
||||||
event.stopPropagation();
|
|
||||||
};
|
|
||||||
|
|
||||||
card.addEventListener('click', toggleCard);
|
|
||||||
toggleIcon.addEventListener('click', toggleCard);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupAppsCarousel() {
|
const grid = card.closest('.games-grid, .about-me-projects-grid');
|
||||||
const carousel = document.getElementById('apps-carousel');
|
|
||||||
const carouselContainer = carousel ? carousel.closest('.carousel-container-full') : null;
|
|
||||||
const items = document.querySelectorAll('#apps-carousel .carousel-item-full');
|
|
||||||
const prevBtn = carouselContainer ? carouselContainer.querySelector('.carousel-nav-full.prev') : null;
|
|
||||||
const nextBtn = carouselContainer ? carouselContainer.querySelector('.carousel-nav-full.next') : null;
|
|
||||||
const screenshotImg = document.getElementById('apps-screenshot-img');
|
|
||||||
const description = document.getElementById('apps-description');
|
|
||||||
const title = document.getElementById('apps-title');
|
|
||||||
const goToAppBtn = document.getElementById('apps-link');
|
|
||||||
|
|
||||||
if (!carousel || items.length === 0) return;
|
|
||||||
|
|
||||||
const services = [
|
|
||||||
{
|
|
||||||
name: 'Gitea',
|
|
||||||
screenshot: '/assets/img/gitea_screenshot.png',
|
|
||||||
description: 'Self-hosted Git service with a simple and lightweight interface.',
|
|
||||||
url: 'https://gitea.douwco.be'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Nextcloud',
|
|
||||||
screenshot: '/assets/img/nextcloud_screenshot.png',
|
|
||||||
description: 'Private file hosting and collaboration platform with powerful features.',
|
|
||||||
url: 'https://nextcloud.douwco.be'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Penpot',
|
|
||||||
screenshot: '/assets/img/penpot_screenshot.png',
|
|
||||||
description: 'Open-source design and prototyping platform for teams.',
|
|
||||||
url: 'https://penpot.douwco.be'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Beszel',
|
|
||||||
screenshot: '/assets/img/beszel_screenshot.png',
|
|
||||||
description: 'Custom service management and monitoring dashboard.',
|
|
||||||
url: 'https://beszel.douwco.be'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Wattif',
|
|
||||||
screenshot: '/assets/img/wattif_screenshot.png',
|
|
||||||
description: 'Energy monitoring and analysis tool for smart homes.',
|
|
||||||
url: 'https://wattif.douwco.be'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
let currentIndex = 0;
|
|
||||||
let isAnimating = false;
|
|
||||||
|
|
||||||
function updateCarousel() {
|
|
||||||
if (isAnimating) return;
|
|
||||||
isAnimating = true;
|
|
||||||
|
|
||||||
items.forEach((item, index) => {
|
|
||||||
const isSelected = index === currentIndex;
|
|
||||||
item.setAttribute('data-selected', isSelected.toString());
|
|
||||||
});
|
|
||||||
|
|
||||||
const service = services[currentIndex];
|
|
||||||
|
|
||||||
if (description) description.classList.remove('active');
|
|
||||||
if (screenshotImg) screenshotImg.classList.remove('active');
|
|
||||||
if (title) title.classList.remove('active');
|
|
||||||
if (goToAppBtn) goToAppBtn.classList.remove('active');
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
if (screenshotImg) {
|
|
||||||
screenshotImg.src = service.screenshot;
|
|
||||||
}
|
|
||||||
if (title) {
|
|
||||||
title.textContent = service.name;
|
|
||||||
}
|
|
||||||
if (description) {
|
|
||||||
description.innerHTML = `<p>${service.description}</p>`;
|
|
||||||
}
|
|
||||||
if (goToAppBtn) {
|
|
||||||
goToAppBtn.href = service.url;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
if (description) description.classList.add('active');
|
|
||||||
if (screenshotImg) screenshotImg.classList.add('active');
|
|
||||||
if (title) title.classList.add('active');
|
|
||||||
if (goToAppBtn) goToAppBtn.classList.add('active');
|
|
||||||
isAnimating = false;
|
|
||||||
}, 50);
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
function rotateCarousel(direction) {
|
|
||||||
if (isAnimating) return;
|
|
||||||
|
|
||||||
currentIndex = (currentIndex + direction + items.length) % items.length;
|
|
||||||
updateCarousel();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevBtn) {
|
|
||||||
prevBtn.addEventListener('click', () => rotateCarousel(-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nextBtn) {
|
|
||||||
nextBtn.addEventListener('click', () => rotateCarousel(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
items.forEach((item, index) => {
|
|
||||||
item.addEventListener('click', () => {
|
|
||||||
if (isAnimating) return;
|
|
||||||
currentIndex = index;
|
|
||||||
updateCarousel();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
let touchStartX = 0;
|
|
||||||
let touchEndX = 0;
|
|
||||||
|
|
||||||
if (carousel) {
|
|
||||||
carousel.addEventListener('touchstart', (e) => {
|
|
||||||
touchStartX = e.changedTouches[0].screenX;
|
|
||||||
}, { passive: true });
|
|
||||||
|
|
||||||
carousel.addEventListener('touchend', (e) => {
|
|
||||||
touchEndX = e.changedTouches[0].screenX;
|
|
||||||
handleSwipe();
|
|
||||||
}, { passive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSwipe() {
|
|
||||||
if (isAnimating) return;
|
|
||||||
|
|
||||||
const swipeThreshold = 50;
|
|
||||||
const diff = touchStartX - touchEndX;
|
|
||||||
|
|
||||||
if (Math.abs(diff) > swipeThreshold) {
|
|
||||||
if (diff > 0) {
|
|
||||||
rotateCarousel(1);
|
|
||||||
} else {
|
|
||||||
rotateCarousel(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (carousel) {
|
|
||||||
carousel.addEventListener('wheel', (e) => {
|
|
||||||
if (isAnimating) return;
|
|
||||||
e.preventDefault();
|
|
||||||
if (e.deltaY > 0) {
|
|
||||||
rotateCarousel(1);
|
|
||||||
} else {
|
|
||||||
rotateCarousel(-1);
|
|
||||||
}
|
|
||||||
}, { passive: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCarousel();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupGameCards() {
|
|
||||||
const gameCards = document.querySelectorAll('.game-card');
|
|
||||||
|
|
||||||
gameCards.forEach(card => {
|
|
||||||
card.addEventListener('click', (event) => {
|
|
||||||
const isExpanded = card.classList.contains('expanded');
|
const isExpanded = card.classList.contains('expanded');
|
||||||
|
|
||||||
if (isExpanded) {
|
if (grid) {
|
||||||
card.classList.remove('expanded');
|
grid.querySelectorAll('.game-card').forEach(c => c.classList.remove('expanded'));
|
||||||
} else {
|
}
|
||||||
gameCards.forEach(c => c.classList.remove('expanded'));
|
|
||||||
|
if (!isExpanded) {
|
||||||
card.classList.add('expanded');
|
card.classList.add('expanded');
|
||||||
}
|
}
|
||||||
|
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
const playButton = card.querySelector('.play-button');
|
const playButton = card.querySelector('.play-button');
|
||||||
if (playButton) {
|
if (playButton) {
|
||||||
playButton.addEventListener('click', (event) => {
|
playButton.addEventListener('click', (event) => {
|
||||||
@@ -261,3 +80,47 @@ function setupGameCards() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupServiceSections() {
|
||||||
|
document.querySelectorAll('.service-section').forEach(section => {
|
||||||
|
const items = section.querySelectorAll('.service-selector-item');
|
||||||
|
const visualImg = section.querySelector('.service-visual-img');
|
||||||
|
const title = section.querySelector('.service-title');
|
||||||
|
const description = section.querySelector('.service-description');
|
||||||
|
const link = section.querySelector('.go-to-app-button');
|
||||||
|
|
||||||
|
if (items.length === 0 || !visualImg || !title || !description || !link) return;
|
||||||
|
|
||||||
|
let isAnimating = false;
|
||||||
|
|
||||||
|
const applyItem = (item) => {
|
||||||
|
visualImg.src = item.dataset.image;
|
||||||
|
visualImg.alt = item.dataset.name;
|
||||||
|
title.textContent = item.dataset.name;
|
||||||
|
description.textContent = item.dataset.description;
|
||||||
|
link.href = item.dataset.url;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectItem = (item) => {
|
||||||
|
if (isAnimating || item.classList.contains('selected')) return;
|
||||||
|
isAnimating = true;
|
||||||
|
|
||||||
|
items.forEach(i => i.classList.toggle('selected', i === item));
|
||||||
|
|
||||||
|
visualImg.classList.add('fading');
|
||||||
|
setTimeout(() => {
|
||||||
|
applyItem(item);
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
visualImg.classList.remove('fading');
|
||||||
|
setTimeout(() => {
|
||||||
|
isAnimating = false;
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
items.forEach(item => {
|
||||||
|
item.addEventListener('click', () => selectItem(item));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
<script src="/assets/js/project-card.js" defer></script>
|
<script src="/assets/js/project-card.js" defer></script>
|
||||||
<script src="/assets/js/index.js" defer></script>
|
<script src="/assets/js/index.js" defer></script>
|
||||||
<script>
|
<script>
|
||||||
// Parse [[text|url]] links in project card content
|
// Parse [[text|url]] links in expanded card descriptions
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const projectCards = document.querySelectorAll('.project-card-content p');
|
const projectCards = document.querySelectorAll('.game-card-full-description');
|
||||||
projectCards.forEach(p => {
|
projectCards.forEach(p => {
|
||||||
const linkPattern = /\[\[([^|\]]+)\|([^\]]+)\]\]/g;
|
const linkPattern = /\[\[([^|\]]+)\|([^\]]+)\]\]/g;
|
||||||
let html = p.innerHTML;
|
let html = p.innerHTML;
|
||||||
@@ -46,6 +46,10 @@
|
|||||||
<img class="icon small" src="/assets/img/icons/projects_icon.png" alt="">
|
<img class="icon small" src="/assets/img/icons/projects_icon.png" alt="">
|
||||||
<p>Douwco <span>Apps</span></p>
|
<p>Douwco <span>Apps</span></p>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="nav_option" href="#hosted">
|
||||||
|
<img class="icon small" src="/assets/img/icons/hosted_icon.svg" alt="">
|
||||||
|
<p>Douwco <span>Hosted</span></p>
|
||||||
|
</a>
|
||||||
<a class="nav_option" href="#games">
|
<a class="nav_option" href="#games">
|
||||||
<img class="icon small" src="/assets/img/icons/devicegamepad2.png" alt="">
|
<img class="icon small" src="/assets/img/icons/devicegamepad2.png" alt="">
|
||||||
<p>My <span>Games</span></p>
|
<p>My <span>Games</span></p>
|
||||||
@@ -99,22 +103,17 @@
|
|||||||
<div class="about-me-section">
|
<div class="about-me-section">
|
||||||
<h2>Currently I'm working on:</h2>
|
<h2>Currently I'm working on:</h2>
|
||||||
<div class="about-me-projects-grid" id="currently-working-grid">
|
<div class="about-me-projects-grid" id="currently-working-grid">
|
||||||
<div class="project-card-new"
|
<div class="game-card">
|
||||||
data-full-desc="For my PhD research at KU Leuven, I'm developing RollAbility, a therapeutic exergame that bridges player autonomy with clinical guidelines to enhance wheelchair training. Developed in collaboration with therapists, RollAbility is a game built onto a clinical protocol and controlled by an actual powered wheelchair. The game connects to the RollAbility App which allows therapists to control the training session in real-time. [[Read the paper|https://dl.acm.org/doi/abs/10.1145/3748621]] or [[watch the video|https://www.youtube.com/watch?v=8y9XFdSN_04]].">
|
<div class="game-card-image contain">
|
||||||
<div class="toggle-icon">+</div>
|
<img src="/assets/img/rollability.png" alt="RollAbility">
|
||||||
<div class="project-card-image-container">
|
|
||||||
<img class="project-card-image" src="/assets/img/rollability.png" alt="RollAbility">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-title-container">
|
<div class="game-card-content">
|
||||||
<div class="project-card-title-background"></div>
|
<h3 class="game-card-title">RollAbility</h3>
|
||||||
<h3 class="project-card-title">RollAbility</h3>
|
<p class="game-card-description">Therapeutic exergame for wheelchair training with clinical protocol integration.
|
||||||
</div>
|
|
||||||
<div class="project-card-short-desc">
|
|
||||||
<p>Therapeutic exergame for wheelchair training with clinical protocol integration.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-content">
|
<div class="game-card-expanded">
|
||||||
<p>For my PhD research at KU Leuven, I'm developing RollAbility, a therapeutic
|
<p class="game-card-full-description">For my PhD research at KU Leuven, I'm developing RollAbility, a therapeutic
|
||||||
exergame that bridges player autonomy with clinical guidelines to enhance
|
exergame that bridges player autonomy with clinical guidelines to enhance
|
||||||
wheelchair training. Developed in collaboration with therapists, RollAbility is
|
wheelchair training. Developed in collaboration with therapists, RollAbility is
|
||||||
a game built onto a clinical protocol and controlled by an actual powered
|
a game built onto a clinical protocol and controlled by an actual powered
|
||||||
@@ -125,22 +124,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="project-card-new"
|
<div class="game-card">
|
||||||
data-full-desc="Alcove is an interreg project between Flemish, Walloon and French universities and hospitals that aims to build a device that detects lung cancer through a breath test. My role is to create a user interface for the device. This UI has to work both on android phones and on embedded linux devices. [[Read more about Alcove|https://alcove.crosss3.eu/nl/]].">
|
<div class="game-card-image contain">
|
||||||
<div class="toggle-icon">+</div>
|
<img src="/assets/img/alcove.png" alt="Alcove">
|
||||||
<div class="project-card-image-container">
|
|
||||||
<img class="project-card-image" src="/assets/img/alcove.png" alt="Alcove">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-title-container">
|
<div class="game-card-content">
|
||||||
<div class="project-card-title-background"></div>
|
<h3 class="game-card-title">Alcove</h3>
|
||||||
<h3 class="project-card-title">Alcove</h3>
|
<p class="game-card-description">Interreg project building a lung cancer detection device with cross-platform UI.
|
||||||
</div>
|
|
||||||
<div class="project-card-short-desc">
|
|
||||||
<p>Interreg project building a lung cancer detection device with cross-platform UI.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-content">
|
<div class="game-card-expanded">
|
||||||
<p>Alcove is an interreg project between Flemish, Walloon and French universities
|
<p class="game-card-full-description">Alcove is an interreg project between Flemish, Walloon and French universities
|
||||||
and hospitals that aims to build a device that detects lung cancer through a
|
and hospitals that aims to build a device that detects lung cancer through a
|
||||||
breath test. My role is to create a user interface for the device. This UI has
|
breath test. My role is to create a user interface for the device. This UI has
|
||||||
to work both on android phones and on embedded linux devices. [[Read more about
|
to work both on android phones and on embedded linux devices. [[Read more about
|
||||||
@@ -153,22 +147,16 @@
|
|||||||
<div class="about-me-section">
|
<div class="about-me-section">
|
||||||
<h2><span class="green-text">However</span> in the past I also worked on:</h2>
|
<h2><span class="green-text">However</span> in the past I also worked on:</h2>
|
||||||
<div class="about-me-projects-grid" id="past-projects-grid">
|
<div class="about-me-projects-grid" id="past-projects-grid">
|
||||||
<div class="project-card-new"
|
<div class="game-card">
|
||||||
data-full-desc="I published two small addons to the godot asset library. One addon called [[Godot Visual Studio Debugger|https://godotengine.org/asset-library/asset/3505]] which allows you to dynamically run the current scene open in the Godot editor directly in Visual Studio Debugger. The second addon called [[Godot Test Scenes|https://godotengine.org/asset-library/asset/3420]] extends the Godot editor with an additional run mode, this mode runs an associated test scene to the currently open one in the editor. They are both open source: [[Godot Test Scenes|https://github.com/DouweRavers/godot_test_scene]] and [[Godot Visual Studio Debugger|https://github.com/DouweRavers/godot_visualstudio_debugger/tree/main]].">
|
<div class="game-card-image contain">
|
||||||
<div class="toggle-icon">+</div>
|
<img src="/assets/img/godot_addons.png" alt="Godot tools">
|
||||||
<div class="project-card-image-container">
|
|
||||||
<img class="project-card-image" src="/assets/img/godot_addons.png"
|
|
||||||
alt="Godot tools">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-title-container">
|
<div class="game-card-content">
|
||||||
<div class="project-card-title-background"></div>
|
<h3 class="game-card-title">Godot Addons</h3>
|
||||||
<h3 class="project-card-title">Godot Addons</h3>
|
<p class="game-card-description">Visual Studio Debugger and Test Scenes addons for the Godot engine.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-short-desc">
|
<div class="game-card-expanded">
|
||||||
<p>Visual Studio Debugger and Test Scenes addons for the Godot engine.</p>
|
<p class="game-card-full-description">I published two small addons to the godot asset library. One addon called [[Godot
|
||||||
</div>
|
|
||||||
<div class="project-card-content">
|
|
||||||
<p>I published two small addons to the godot asset library. One addon called [[Godot
|
|
||||||
Visual Studio Debugger|https://godotengine.org/asset-library/asset/3505]] which
|
Visual Studio Debugger|https://godotengine.org/asset-library/asset/3505]] which
|
||||||
allows you to dynamically run the current scene open in the Godot editor
|
allows you to dynamically run the current scene open in the Godot editor
|
||||||
directly in Visual Studio Debugger. The second addon called [[Godot Test
|
directly in Visual Studio Debugger. The second addon called [[Godot Test
|
||||||
@@ -182,48 +170,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="project-card-new"
|
<div class="game-card">
|
||||||
data-full-desc="The project [[Kingfisher|https://kingfisher-game.com/]] is a game built by PossilbyPixels that raises awareness about Autism. It is a story-based action / puzzle game for PC and mobile. My role in the project was to build minigames that occur throughout the story and also to develop tools to help development. One of the tools I made for this project was a suburban neighborhood generator. This tool allowed us to draw streets in the editor which then were automatically filled with procedurally generated houses and gardens. You can see a demo in [[this video|https://nextcloud.douwco.be/s/A8XPCgQkTNDinjE]]. You can wishlist the game on [[Steam|https://store.steampowered.com/app/4197280/Kingfisher_An_Autism_Tale/]]!">
|
<div class="game-card-image contain">
|
||||||
<div class="toggle-icon">+</div>
|
<img src="/assets/img/kingfisher.png" alt="Kingfisher">
|
||||||
<div class="project-card-image-container">
|
|
||||||
<img class="project-card-image" src="/assets/img/kingfisher.png" alt="Kingfisher">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-title-container">
|
<div class="game-card-content">
|
||||||
<div class="project-card-title-background"></div>
|
<h3 class="game-card-title">Kingfisher</h3>
|
||||||
<h3 class="project-card-title">Kingfisher</h3>
|
<p class="game-card-description">Story-based action/puzzle game raising awareness about Autism.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-short-desc">
|
<div class="game-card-expanded">
|
||||||
<p>Story-based action/puzzle game raising awareness about Autism.</p>
|
<p class="game-card-full-description">The project [[Kingfisher|https://kingfisher-game.com/]] is a game built by
|
||||||
</div>
|
|
||||||
<div class="project-card-content">
|
|
||||||
<p>The project [[Kingfisher|https://kingfisher-game.com/]] is a game built by
|
|
||||||
PossilbyPixels that raises awareness about Autism. It is a story-based action /
|
PossilbyPixels that raises awareness about Autism. It is a story-based action /
|
||||||
puzzle game for PC and mobile. My role in the project was to build minigames
|
puzzle game for PC and mobile. My role in the project was to build minigames
|
||||||
that occur throughout the story and also to develop tools to help development.
|
that occur throughout the story and also to develop tools to help development.
|
||||||
One of the tools I made for this project was a suburban neighborhood generator.
|
One of the tools I made for this project was a suburban neighborhood generator.
|
||||||
This tool allowed us to draw streets in the editor which then were automatically
|
This tool allowed us to draw streets in the editor which then were automatically
|
||||||
filled with procedurally generated houses and gardens. You can see a demo in
|
filled with procedurally generated houses and gardens. You can see a demo in
|
||||||
[[this video|https://nextcloud.douwco.be/s/A8XPCgQkTNDinjE]]. You can wishlist the game on
|
[[this video|https://nextcloud.douwco.be/s/A8XPCgQkTNDinjE]] or you can wishlist the game on
|
||||||
[[Steam|https://store.steampowered.com/app/4197280/Kingfisher_An_Autism_Tale/]]!
|
[[Steam|https://store.steampowered.com/app/4197280/Kingfisher_An_Autism_Tale/]]!
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="project-card-new"
|
<div class="game-card">
|
||||||
data-full-desc="Between 2019 and 2024, I participated regularly in the Ludum Dare event. This event was all about creating games under tight deadlines of 48 hours and with no external resources such as audio or 3D art. After each event I published my game on [[Itch.io|https://coffeeplanet.itch.io/]] where they are still available for play. In the future I want to migrate them to this website. I want to especially highlight [[Cobor goes farming|https://coffeeplanet.itch.io/ludum-dare-51]] and [[Windy the Indie|https://coffeeplanet.itch.io/windy-the-indie]].">
|
<div class="game-card-image contain">
|
||||||
<div class="toggle-icon">+</div>
|
<img src="/assets/img/ludum_dare.png" alt="Ludum Dare">
|
||||||
<div class="project-card-image-container">
|
|
||||||
<img class="project-card-image" src="/assets/img/ludum_dare.png" alt="Ludum Dare">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-title-container">
|
<div class="game-card-content">
|
||||||
<div class="project-card-title-background"></div>
|
<h3 class="game-card-title">Ludum Dare</h3>
|
||||||
<h3 class="project-card-title">Ludum Dare</h3>
|
<p class="game-card-description">Game jam participation with 48-hour deadlines and no external resources.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-short-desc">
|
<div class="game-card-expanded">
|
||||||
<p>Game jam participation with 48-hour deadlines and no external resources.</p>
|
<p class="game-card-full-description">Between 2019 and 2024, I participated regularly in the Ludum Dare event. This
|
||||||
</div>
|
|
||||||
<div class="project-card-content">
|
|
||||||
<p>Between 2019 and 2024, I participated regularly in the Ludum Dare event. This
|
|
||||||
event was all about creating games under tight deadlines of 48 hours and with no
|
event was all about creating games under tight deadlines of 48 hours and with no
|
||||||
external resources such as audio or 3D art. After each event I published my game
|
external resources such as audio or 3D art. After each event I published my game
|
||||||
on [[Itch.io|https://coffeeplanet.itch.io/]] where they are still available for
|
on [[Itch.io|https://coffeeplanet.itch.io/]] where they are still available for
|
||||||
@@ -233,21 +211,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="project-card-new"
|
<div class="game-card">
|
||||||
data-full-desc="For my Masterthesis at Group T KU Leuven I made a tool for Unity that creates a procedural planet terrain. The main challenge of this project was to optimize the algorithm to work as fast as possible. For this I leveraged GPU compute shaders to process large chunks of data at once. You can find the tool and my thesis paper [[here|https://github.com/DouweRavers/ThesisProject-ThePlanetEngine/tree/main]].">
|
<div class="game-card-image contain">
|
||||||
<div class="toggle-icon">+</div>
|
<img src="/assets/img/thesis_tool.png" alt="Planet Tool">
|
||||||
<div class="project-card-image-container">
|
|
||||||
<img class="project-card-image" src="/assets/img/thesis_tool.png" alt="Planet Tool">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-title-container">
|
<div class="game-card-content">
|
||||||
<div class="project-card-title-background"></div>
|
<h3 class="game-card-title">Masterthesis: Planet Tool</h3>
|
||||||
<h3 class="project-card-title">Masterthesis: Planet Tool</h3>
|
<p class="game-card-description">Procedural planet terrain generation with GPU compute shaders.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-short-desc">
|
<div class="game-card-expanded">
|
||||||
<p>Procedural planet terrain generation with GPU compute shaders.</p>
|
<p class="game-card-full-description">For my Masterthesis at Group T KU Leuven I made a tool for Unity that creates a
|
||||||
</div>
|
|
||||||
<div class="project-card-content">
|
|
||||||
<p>For my Masterthesis at Group T KU Leuven I made a tool for Unity that creates a
|
|
||||||
procedural planet terrain. The main challenge of this project was to optimize
|
procedural planet terrain. The main challenge of this project was to optimize
|
||||||
the algorithm to work as fast as possible. For this I leveraged GPU compute
|
the algorithm to work as fast as possible. For this I leveraged GPU compute
|
||||||
shaders to process large chunks of data at once. You can find the tool and my
|
shaders to process large chunks of data at once. You can find the tool and my
|
||||||
@@ -257,22 +230,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="project-card-new"
|
<div class="game-card">
|
||||||
data-full-desc="In another project during my masters we explored gesture-based interactions in virtual environments. For this project we developed a prototype using Unity and custom wearable hardware to enable magic-like hand gestures for interfacing with a story game. See the game [[here|https://nextcloud.douwco.be/s/STAkfXXpyNpDfrp]] or you can find the game and paper [[here|https://github.com/DouweRavers/MasterProject-WizardsGlovesAdventure/tree/main]].">
|
<div class="game-card-image contain">
|
||||||
<div class="toggle-icon">+</div>
|
<img src="/assets/img/wizard_gloves.png" alt="Wizard Gloves">
|
||||||
<div class="project-card-image-container">
|
|
||||||
<img class="project-card-image" src="/assets/img/wizard_gloves.png"
|
|
||||||
alt="Wizard Gloves">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-title-container">
|
<div class="game-card-content">
|
||||||
<div class="project-card-title-background"></div>
|
<h3 class="game-card-title">Masterproject: Wizard Gloves</h3>
|
||||||
<h3 class="project-card-title">Masterproject: Wizard Gloves</h3>
|
<p class="game-card-description">Gesture-based interaction with wearable hardware for virtual environments.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-card-short-desc">
|
<div class="game-card-expanded">
|
||||||
<p>Gesture-based interaction with wearable hardware for virtual environments.</p>
|
<p class="game-card-full-description">In another project during my masters we explored gesture-based interactions in
|
||||||
</div>
|
|
||||||
<div class="project-card-content">
|
|
||||||
<p>In another project during my masters we explored gesture-based interactions in
|
|
||||||
virtual environments. For this project we developed a prototype using Unity and
|
virtual environments. For this project we developed a prototype using Unity and
|
||||||
custom wearable hardware to enable magic-like hand gestures for interfacing with
|
custom wearable hardware to enable magic-like hand gestures for interfacing with
|
||||||
a story game. See the game [[here|https://nextcloud.douwco.be/s/STAkfXXpyNpDfrp]] or you can
|
a story game. See the game [[here|https://nextcloud.douwco.be/s/STAkfXXpyNpDfrp]] or you can
|
||||||
@@ -284,58 +251,92 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="apps" class="scroll_section">
|
<section id="apps" class="scroll_section">
|
||||||
<h1>Douwco Apps</h1>
|
<h1>Douwco Apps</h1>
|
||||||
<div class="apps-container">
|
<div class="service-section">
|
||||||
<div class="carousel-container-full">
|
<div class="service-selector">
|
||||||
<div class="carousel-track-full" id="apps-carousel">
|
<button class="service-selector-item selected" data-name="Wattif"
|
||||||
<div class="carousel-item-full" data-index="0" data-selected="true">
|
data-description="Energy monitoring and analysis tool for smart homes."
|
||||||
<div class="service-icon-large" style="--icon-bg: #e94e36;">
|
data-url="https://wattif.douwco.be" data-image="/assets/img/wattif_screenshot.png">
|
||||||
<span class="icon-placeholder-large">G</span>
|
<span class="service-selector-icon" style="--icon-bg: #7d3c98;">
|
||||||
</div>
|
<img src="/assets/img/wattif_screenshot.png" alt="Wattif logo">
|
||||||
<span class="service-label-large">Gitea</span>
|
</span>
|
||||||
</div>
|
<span class="service-selector-label">Wattif</span>
|
||||||
<div class="carousel-item-full" data-index="1">
|
</button>
|
||||||
<div class="service-icon-large" style="--icon-bg: #0082c9;">
|
<button class="service-selector-item" data-name="Findash"
|
||||||
<span class="icon-placeholder-large">N</span>
|
data-description="A self-hosted finance dashboard for tracking and analysing my personal spending."
|
||||||
</div>
|
data-url="https://findash.douwco.be" data-image="/assets/img/findash_dashboard.svg">
|
||||||
<span class="service-label-large">Nextcloud</span>
|
<span class="service-selector-icon" style="--icon-bg: #3fb950;">
|
||||||
</div>
|
<img src="/assets/img/findash_dashboard.svg" alt="Findash logo">
|
||||||
<div class="carousel-item-full" data-index="2">
|
</span>
|
||||||
<div class="service-icon-large" style="--icon-bg: #f16521;">
|
<span class="service-selector-label">Findash</span>
|
||||||
<span class="icon-placeholder-large">P</span>
|
</button>
|
||||||
</div>
|
|
||||||
<span class="service-label-large">Penpot</span>
|
|
||||||
</div>
|
|
||||||
<div class="carousel-item-full" data-index="3">
|
|
||||||
<div class="service-icon-large" style="--icon-bg: #4a90d9;">
|
|
||||||
<span class="icon-placeholder-large">B</span>
|
|
||||||
</div>
|
|
||||||
<span class="service-label-large">Beszel</span>
|
|
||||||
</div>
|
|
||||||
<div class="carousel-item-full" data-index="4">
|
|
||||||
<div class="service-icon-large" style="--icon-bg: #7d3c98;">
|
|
||||||
<span class="icon-placeholder-large">W</span>
|
|
||||||
</div>
|
|
||||||
<span class="service-label-large">Wattif</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button class="carousel-nav-full prev" aria-label="Previous">❮</button>
|
|
||||||
<button class="carousel-nav-full next" aria-label="Next">❯</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="service-detail-full" id="apps-detail">
|
<div class="service-detail">
|
||||||
<div class="service-screenshot-full">
|
<div class="service-visual">
|
||||||
<img src="/assets/img/gitea_screenshot.png" alt="Gitea screenshot" id="apps-screenshot-img">
|
<img src="/assets/img/wattif_screenshot.png" alt="Wattif" class="service-visual-img">
|
||||||
</div>
|
</div>
|
||||||
<div class="service-info-full">
|
<div class="service-info">
|
||||||
<h2 class="service-title" id="apps-title">Gitea</h2>
|
<h2 class="service-title">Wattif</h2>
|
||||||
<div class="service-description-full" id="apps-description">
|
<p class="service-description">Energy monitoring and analysis tool for smart homes.</p>
|
||||||
<p>Self-hosted Git service with a simple and lightweight interface.</p>
|
<a href="https://wattif.douwco.be" target="_blank" rel="noopener"
|
||||||
</div>
|
class="go-to-app-button">Go to App</a>
|
||||||
<a href="https://gitea.douwco.be" target="_blank" rel="noopener" class="go-to-app-button"
|
</div>
|
||||||
id="apps-link">Go to App</a>
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="hosted" class="scroll_section">
|
||||||
|
<h1>Douwco Hosted</h1>
|
||||||
|
<div class="service-section" data-visit-label="Visit">
|
||||||
|
<div class="service-selector">
|
||||||
|
<button class="service-selector-item selected" data-name="Nextcloud"
|
||||||
|
data-description="Self-hosted file storage, calendar, contacts and collaboration suite."
|
||||||
|
data-url="https://nextcloud.douwco.be"
|
||||||
|
data-image="/assets/img/nextcloud_screenshot.png">
|
||||||
|
<span class="service-selector-icon" style="--icon-bg: #0082c9;">
|
||||||
|
<img src="/assets/img/nextcloud_screenshot.png" alt="Nextcloud logo">
|
||||||
|
</span>
|
||||||
|
<span class="service-selector-label">Nextcloud</span>
|
||||||
|
</button>
|
||||||
|
<button class="service-selector-item" data-name="Gitea"
|
||||||
|
data-description="Lightweight self-hosted Git service with a web interface for my code repositories."
|
||||||
|
data-url="https://gitea.douwco.be" data-image="/assets/img/gitea_screenshot.png">
|
||||||
|
<span class="service-selector-icon" style="--icon-bg: #e94e36;">
|
||||||
|
<img src="/assets/img/gitea_screenshot.png" alt="Gitea logo">
|
||||||
|
</span>
|
||||||
|
<span class="service-selector-label">Gitea</span>
|
||||||
|
</button>
|
||||||
|
<button class="service-selector-item" data-name="Beszel"
|
||||||
|
data-description="Lightweight server monitoring: real-time metrics and alerts for my servers."
|
||||||
|
data-url="https://beszel.douwco.be" data-image="/assets/img/beszel_screenshot.png">
|
||||||
|
<span class="service-selector-icon" style="--icon-bg: #4a90d9;">
|
||||||
|
<img src="/assets/img/beszel_screenshot.png" alt="Beszel logo">
|
||||||
|
</span>
|
||||||
|
<span class="service-selector-label">Beszel</span>
|
||||||
|
</button>
|
||||||
|
<button class="service-selector-item" data-name="Penpot"
|
||||||
|
data-description="Open-source, self-hosted design and prototyping platform (Figma alternative)."
|
||||||
|
data-url="https://penpot.douwco.be" data-image="/assets/img/penpot_screenshot.png">
|
||||||
|
<span class="service-selector-icon" style="--icon-bg: #f16521;">
|
||||||
|
<img src="/assets/img/penpot_screenshot.png" alt="Penpot logo">
|
||||||
|
</span>
|
||||||
|
<span class="service-selector-label">Penpot</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="service-detail">
|
||||||
|
<div class="service-visual">
|
||||||
|
<img src="/assets/img/nextcloud_screenshot.png" alt="Nextcloud" class="service-visual-img">
|
||||||
|
</div>
|
||||||
|
<div class="service-info">
|
||||||
|
<h2 class="service-title">Nextcloud</h2>
|
||||||
|
<p class="service-description">Self-hosted file storage, calendar, contacts and
|
||||||
|
collaboration suite.</p>
|
||||||
|
<a href="https://nextcloud.douwco.be" target="_blank" rel="noopener"
|
||||||
|
class="go-to-app-button">Visit</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -345,115 +346,298 @@
|
|||||||
<h1>My Games</h1>
|
<h1>My Games</h1>
|
||||||
<div class="games-container">
|
<div class="games-container">
|
||||||
<div class="games-grid" id="games-grid">
|
<div class="games-grid" id="games-grid">
|
||||||
<div class="game-card" data-title="Ludum Dare 51: Cobor goes farming" data-year="2022"
|
<div class="game-card">
|
||||||
data-description="A farming game made in 48 hours for Ludum Dare 51."
|
|
||||||
data-url="https://coffeeplanet.itch.io/ludum-dare-51">
|
|
||||||
<div class="game-card-image">
|
<div class="game-card-image">
|
||||||
<img src="/assets/img/games/ludum51_placeholder.png" alt="Cobor goes farming">
|
<img src="/assets/img/games/romants.png" alt="RomAnts cover">
|
||||||
|
</div>
|
||||||
|
<div class="game-card-content">
|
||||||
|
<h3 class="game-card-title">RomAnts</h3>
|
||||||
|
<div class="game-card-meta">
|
||||||
|
<span class="game-year-badge">2024</span>
|
||||||
|
<span class="game-genre">Strategy / Tower Defense</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">A 3D tower defence game with a Roman flavour: lead the
|
||||||
|
RomAnts in defending the Temple of Antonia from the BarbariAnts.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/romants" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play</a>
|
||||||
|
</div>
|
||||||
|
<div class="game-card-expanded">
|
||||||
|
<p class="game-card-full-description">A tower defense game where you lead the brave RomAnts
|
||||||
|
in defending their sacred Temple of Antonia from the ruthless BarbariAnts. As
|
||||||
|
General, you recruit workers and soldiers, gather resources, and build guard towers
|
||||||
|
while the BarbariAnt forces grow stronger over time. Made in 48 hours for Ludum Dare
|
||||||
|
56 (theme: Tiny Creatures).</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/romants" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="game-card">
|
||||||
|
<div class="game-card-image">
|
||||||
|
<img src="/assets/img/games/coral-conquest.jpg" alt="Coral Conquest cover">
|
||||||
|
</div>
|
||||||
|
<div class="game-card-content">
|
||||||
|
<h3 class="game-card-title">Coral Conquest</h3>
|
||||||
|
<div class="game-card-meta">
|
||||||
|
<span class="game-year-badge">2024</span>
|
||||||
|
<span class="game-genre">Strategy</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">Build and manage an underwater settlement with houses,
|
||||||
|
farms and fish.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/coral-conquest" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play</a>
|
||||||
|
</div>
|
||||||
|
<div class="game-card-expanded">
|
||||||
|
<p class="game-card-full-description">Build and manage an underwater settlement: place
|
||||||
|
houses, farms and fish, and keep your coral city growing. Select buildings and fish
|
||||||
|
and move them where they are needed. Made for the FishFest game jam; downloadable for
|
||||||
|
Windows and Linux.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/coral-conquest" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="game-card">
|
||||||
|
<div class="game-card-image">
|
||||||
|
<img src="/assets/img/games/cropcopter.jpg" alt="CropCopter cover">
|
||||||
|
</div>
|
||||||
|
<div class="game-card-content">
|
||||||
|
<h3 class="game-card-title">CropCopter</h3>
|
||||||
|
<div class="game-card-meta">
|
||||||
|
<span class="game-year-badge">2023</span>
|
||||||
|
<span class="game-genre">Ludum Dare 53</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">Made with Godot for Ludum Dare 53: fly the CropCopter
|
||||||
|
and deliver the harvest.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/cropcopter" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play</a>
|
||||||
|
</div>
|
||||||
|
<div class="game-card-expanded">
|
||||||
|
<p class="game-card-full-description">Made with Godot for Ludum Dare 53 (theme: Delivery).
|
||||||
|
Fly the CropCopter and deliver the harvest — playable in the browser, with downloads
|
||||||
|
available for Windows and Linux.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/cropcopter" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="game-card">
|
||||||
|
<div class="game-card-image">
|
||||||
|
<img src="/assets/img/games/cobor-goes-farming.jpg" alt="Cobor goes farming cover">
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-content">
|
<div class="game-card-content">
|
||||||
<h3 class="game-card-title">Cobor goes farming</h3>
|
<h3 class="game-card-title">Cobor goes farming</h3>
|
||||||
<p class="game-card-description">A farming game made in 48 hours.</p>
|
<div class="game-card-meta">
|
||||||
<span class="game-year-badge">2022</span>
|
<span class="game-year-badge">2022</span>
|
||||||
</div>
|
<span class="game-genre">Educational / Puzzle</span>
|
||||||
<div class="game-card-expanded">
|
</div>
|
||||||
<p class="game-card-full-description">A farming game made in 48 hours for Ludum Dare 51.
|
<p class="game-card-description">An assembly-inspired programming puzzle game.</p>
|
||||||
Features crop planting, animal husbandry, and resource management.</p>
|
|
||||||
<a href="https://coffeeplanet.itch.io/ludum-dare-51" target="_blank" rel="noopener"
|
<a href="https://coffeeplanet.itch.io/ludum-dare-51" target="_blank" rel="noopener"
|
||||||
class="play-button">Play Game</a>
|
class="play-button">Play</a>
|
||||||
|
</div>
|
||||||
|
<div class="game-card-expanded">
|
||||||
|
<p class="game-card-full-description">A puzzle programming game made for Ludum Dare 51:
|
||||||
|
write a program that performs three challenges, each within 10 seconds. The
|
||||||
|
programming language is assembly-like, where the key is to manipulate data in the
|
||||||
|
memory array. Three levels go from easy to hard.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/ludum-dare-51" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="game-card" data-title="Ludum Dare 50" data-year="2022"
|
<div class="game-card">
|
||||||
data-description="A puzzle platformer with a unique mechanic."
|
|
||||||
data-url="https://coffeeplanet.itch.io/">
|
|
||||||
<div class="game-card-image">
|
<div class="game-card-image">
|
||||||
<img src="/assets/img/games/ludum50_placeholder.png" alt="Ludum Dare 50">
|
<img src="/assets/img/games/for-king-for-freedom-and-for-law.jpg"
|
||||||
|
alt="For King, for Freedom and for Law! cover">
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-content">
|
<div class="game-card-content">
|
||||||
<h3 class="game-card-title">Platform Puzzler</h3>
|
<h3 class="game-card-title">For King, for Freedom and for Law!</h3>
|
||||||
<p class="game-card-description">A puzzle platformer game.</p>
|
<div class="game-card-meta">
|
||||||
<span class="game-year-badge">2022</span>
|
<span class="game-year-badge">2021</span>
|
||||||
|
<span class="game-genre">Strategy</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">Fight your way through the Belgian revolution of 1831.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/ludumdare48" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-expanded">
|
<div class="game-card-expanded">
|
||||||
<p class="game-card-full-description">A puzzle platformer with a unique mechanic. Solve
|
<p class="game-card-full-description">You play as a military commander during the Belgian
|
||||||
puzzles by manipulating the environment and your character.</p>
|
revolution of 1831: save soldiers, guide them into battle, and drive the Dutch forces
|
||||||
<a href="https://coffeeplanet.itch.io/" target="_blank" rel="noopener"
|
back to the northern borders. Based on the Ten Days' Campaign and made for Ludum Dare
|
||||||
class="play-button">Play Game</a>
|
48.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/ludumdare48" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="game-card" data-title="Ludum Dare 49" data-year="2021"
|
<div class="game-card">
|
||||||
data-description="A top-down adventure with exploration."
|
|
||||||
data-url="https://coffeeplanet.itch.io/">
|
|
||||||
<div class="game-card-image">
|
<div class="game-card-image">
|
||||||
<img src="/assets/img/games/ludum49_placeholder.png" alt="Ludum Dare 49">
|
<img src="/assets/img/games/tanks-n-robots.jpg" alt="Tanks 'n Robots cover">
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-content">
|
<div class="game-card-content">
|
||||||
<h3 class="game-card-title">Adventure Quest</h3>
|
<h3 class="game-card-title">Tanks 'n Robots</h3>
|
||||||
<p class="game-card-description">A top-down adventure game.</p>
|
<div class="game-card-meta">
|
||||||
<span class="game-year-badge">2021</span>
|
<span class="game-year-badge">2021</span>
|
||||||
|
<span class="game-genre">Shooter</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">The ultimate battle of robots versus the raw power of
|
||||||
|
heavy military vehicles.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/ludum-dare-49" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-expanded">
|
<div class="game-card-expanded">
|
||||||
<p class="game-card-full-description">A top-down adventure with exploration, enemies, and
|
<p class="game-card-full-description">After an unstable Windows update lets AI take over
|
||||||
treasure collection. Made in 48 hours for Ludum Dare 49.</p>
|
the world, all technology is under its control. Luckily, your deprecated military
|
||||||
<a href="https://coffeeplanet.itch.io/" target="_blank" rel="noopener"
|
vehicle still works. Reconquer the world by eliminating the evil AI! Made for Ludum
|
||||||
class="play-button">Play Game</a>
|
Dare 49.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/ludum-dare-49" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="game-card" data-title="Ludum Dare 48" data-year="2021"
|
<div class="game-card">
|
||||||
data-description="A minimalist arcade game." data-url="https://coffeeplanet.itch.io/">
|
|
||||||
<div class="game-card-image">
|
<div class="game-card-image">
|
||||||
<img src="/assets/img/games/ludum48_placeholder.png" alt="Ludum Dare 48">
|
<img src="/assets/img/games/gerstenat.png" alt="Gerstenat cover">
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-content">
|
<div class="game-card-content">
|
||||||
<h3 class="game-card-title">Arcade Shooter</h3>
|
<h3 class="game-card-title">Gerstenat</h3>
|
||||||
<p class="game-card-description">A minimalist arcade shooter.</p>
|
<div class="game-card-meta">
|
||||||
<span class="game-year-badge">2021</span>
|
<span class="game-year-badge">2021</span>
|
||||||
|
<span class="game-genre">App</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">An app that simulates the drinking games me and my
|
||||||
|
friends play when partying.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/gerstenat" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-expanded">
|
<div class="game-card-expanded">
|
||||||
<p class="game-card-full-description">A minimalist arcade shooter with wave-based gameplay
|
<p class="game-card-full-description">Gerstenat simulates the drinking games that me and
|
||||||
and power-ups. Created for Ludum Dare 48.</p>
|
my friends play when partying. The app is still in development; an Android APK is
|
||||||
<a href="https://coffeeplanet.itch.io/" target="_blank" rel="noopener"
|
available to try it out.</p>
|
||||||
class="play-button">Play Game</a>
|
<a href="https://coffeeplanet.itch.io/gerstenat" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="game-card" data-title="Ludum Dare 47" data-year="2020"
|
<div class="game-card">
|
||||||
data-description="A story-driven puzzle game." data-url="https://coffeeplanet.itch.io/">
|
|
||||||
<div class="game-card-image">
|
<div class="game-card-image">
|
||||||
<img src="/assets/img/games/ludum47_placeholder.png" alt="Ludum Dare 47">
|
<img src="/assets/img/games/break-the-isolation.png" alt="Break the Isolation cover">
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-content">
|
<div class="game-card-content">
|
||||||
<h3 class="game-card-title">Story Puzzles</h3>
|
<h3 class="game-card-title">Break the Isolation</h3>
|
||||||
<p class="game-card-description">A story-driven puzzle game.</p>
|
<div class="game-card-meta">
|
||||||
<span class="game-year-badge">2020</span>
|
<span class="game-year-badge">2020</span>
|
||||||
|
<span class="game-genre">Strategy</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">As a virus, break the city out of isolation! But watch
|
||||||
|
out for the other viruses.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/break-the-isolation" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-expanded">
|
<div class="game-card-expanded">
|
||||||
<p class="game-card-full-description">A story-driven puzzle game where each level reveals
|
<p class="game-card-full-description">As a virus, try to infect the city in isolation —
|
||||||
part of the narrative. Made in 48 hours for Ludum Dare 47.</p>
|
but watch out for the other viruses trying to beat you to it. Works even on
|
||||||
<a href="https://coffeeplanet.itch.io/" target="_blank" rel="noopener"
|
smartphones. Made for the Major Jam: Isolation.</p>
|
||||||
class="play-button">Play Game</a>
|
<a href="https://coffeeplanet.itch.io/break-the-isolation" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="game-card" data-title="Windy the Indie" data-year="2024"
|
<div class="game-card">
|
||||||
data-description="A platformer about indie game development."
|
|
||||||
data-url="https://coffeeplanet.itch.io/windy-the-indie">
|
|
||||||
<div class="game-card-image">
|
<div class="game-card-image">
|
||||||
<img src="/assets/img/games/windy_placeholder.png" alt="Windy the Indie">
|
<img src="/assets/img/games/pandaemic.jpg" alt="Pandæmic cover">
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-content">
|
<div class="game-card-content">
|
||||||
<h3 class="game-card-title">Windy the Indie</h3>
|
<h3 class="game-card-title">Pandæmic</h3>
|
||||||
<p class="game-card-description">A platformer about indie development.</p>
|
<div class="game-card-meta">
|
||||||
<span class="game-year-badge">2024</span>
|
<span class="game-year-badge">2020</span>
|
||||||
|
<span class="game-genre">Simulation</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">Build the most effective panda enclosure and collect as
|
||||||
|
many pandas as you can before the time limit ends.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/pandmic" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="game-card-expanded">
|
<div class="game-card-expanded">
|
||||||
<p class="game-card-full-description">A platformer game about the journey of indie game
|
<p class="game-card-full-description">You were chosen as manager of the new Utrecht panda
|
||||||
development. Features challenging levels and a touching story.</p>
|
breeding center. Design the most effective panda enclosure: build huts, sources of
|
||||||
|
food and water, and even toys to make them happy. Work fast before the time limit
|
||||||
|
ends — and beware THE RABBITS!</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/pandmic" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="game-card">
|
||||||
|
<div class="game-card-image">
|
||||||
|
<img src="/assets/img/games/loopedy-island.jpg" alt="Loopedy Island cover">
|
||||||
|
</div>
|
||||||
|
<div class="game-card-content">
|
||||||
|
<h3 class="game-card-title">Loopedy Island</h3>
|
||||||
|
<div class="game-card-meta">
|
||||||
|
<span class="game-year-badge">2020</span>
|
||||||
|
<span class="game-genre">Racing</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">A race game created in 48 hours for Ludum Dare 47.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/ludumdare47" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play</a>
|
||||||
|
</div>
|
||||||
|
<div class="game-card-expanded">
|
||||||
|
<p class="game-card-full-description">A racing game created in 48 hours for Ludum Dare 47.
|
||||||
|
Jump through the loops for points and avoid hitting walls and cars. Playable in the
|
||||||
|
browser, or download the Windows executable for better performance and fancier
|
||||||
|
graphics.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/ludumdare47" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="game-card">
|
||||||
|
<div class="game-card-image">
|
||||||
|
<img src="/assets/img/games/windy-the-indie.jpg" alt="Windy The Indie cover">
|
||||||
|
</div>
|
||||||
|
<div class="game-card-content">
|
||||||
|
<h3 class="game-card-title">Windy The Indie</h3>
|
||||||
|
<div class="game-card-meta">
|
||||||
|
<span class="game-year-badge">2020</span>
|
||||||
|
<span class="game-genre">Action</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">Play as Windy, a Native American boy defending his home
|
||||||
|
forest from mean rednecks with his bow.</p>
|
||||||
<a href="https://coffeeplanet.itch.io/windy-the-indie" target="_blank" rel="noopener"
|
<a href="https://coffeeplanet.itch.io/windy-the-indie" target="_blank" rel="noopener"
|
||||||
class="play-button">Play Game</a>
|
class="play-button">Play</a>
|
||||||
|
</div>
|
||||||
|
<div class="game-card-expanded">
|
||||||
|
<p class="game-card-full-description">Windy is a Native American boy struggling to
|
||||||
|
protect his home forest from mean rednecks. Desperately, with his bow, he tries to
|
||||||
|
shoot as many of them down as he can — can you keep the forest alive? Made for Ludum
|
||||||
|
Dare 46; also available on Google Play.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/windy-the-indie" target="_blank" rel="noopener"
|
||||||
|
class="play-button">Play on itch.io</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="game-card">
|
||||||
|
<div class="game-card-image">
|
||||||
|
<img src="/assets/img/games/the-desert-of-nothingness.jpg"
|
||||||
|
alt="LD45: The desert of nothingness cover">
|
||||||
|
</div>
|
||||||
|
<div class="game-card-content">
|
||||||
|
<h3 class="game-card-title">LD45: The desert of nothingness</h3>
|
||||||
|
<div class="game-card-meta">
|
||||||
|
<span class="game-year-badge">2019</span>
|
||||||
|
<span class="game-genre">Ludum Dare 45</span>
|
||||||
|
</div>
|
||||||
|
<p class="game-card-description">Plant planting game made for Ludum Dare 45. My first
|
||||||
|
finished game ever.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/the-desert-of-nothingness" target="_blank"
|
||||||
|
rel="noopener" class="play-button">Play</a>
|
||||||
|
</div>
|
||||||
|
<div class="game-card-expanded">
|
||||||
|
<p class="game-card-full-description">A plant planting game made for Ludum Dare 45 (theme:
|
||||||
|
Start with nothing). My first finished game ever.</p>
|
||||||
|
<a href="https://coffeeplanet.itch.io/the-desert-of-nothingness" target="_blank"
|
||||||
|
rel="noopener" class="play-button">Play on itch.io</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -469,4 +653,4 @@
|
|||||||
</footer>
|
</footer>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||