Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import type {Config} from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';

const config: Config = {
title: 'EduIDE Docs',
tagline: 'Documentation and product guidance for EduIDE',
title: 'edu ide — The IDE built for learning',
tagline: 'A browser-based development environment designed for education',
favicon: 'img/logo.svg',
stylesheets: [
'https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,400;0,500;0,600;1,400&family=Space+Grotesk:wght@600;700&display=swap',
],
future: {
v4: true,
},
Expand Down
43 changes: 43 additions & 0 deletions src/components/landing/Button.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.btnPrimary {
background: var(--landing-teal);
color: #fff !important;
border: none;
padding: 13px 24px;
border-radius: 10px;
font-size: 15px;
font-weight: 500;
cursor: pointer;
font-family: 'DM Sans', sans-serif;
transition: background 0.15s, transform 0.1s;
text-decoration: none !important;
display: inline-block;
}

.btnPrimary:hover {
background: var(--landing-teal-dark);
color: #fff !important;
}

.btnPrimary:active {
transform: scale(0.98);
}

.btnSecondary {
background: transparent;
color: var(--landing-text) !important;
border: 0.5px solid var(--landing-border-med);
padding: 13px 24px;
border-radius: 10px;
font-size: 15px;
font-weight: 500;
cursor: pointer;
font-family: 'DM Sans', sans-serif;
transition: background 0.15s;
text-decoration: none !important;
display: inline-block;
}

.btnSecondary:hover {
background: var(--landing-surface);
color: var(--landing-text) !important;
}
30 changes: 30 additions & 0 deletions src/components/landing/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import Link from '@docusaurus/Link';
import styles from './Button.module.css';

interface ButtonProps {
variant: 'primary' | 'secondary';
to: string;
target?: string;
children: React.ReactNode;
}

const Button = React.memo<ButtonProps>(function Button({
variant,
to,
target,
children,
}) {
return (
<Link
className={variant === 'primary' ? styles.btnPrimary : styles.btnSecondary}
to={to}
target={target}
{...(target === '_blank' ? { rel: 'noopener noreferrer' } : {})}
>
{children}
</Link>
);
});

export default Button;
40 changes: 40 additions & 0 deletions src/components/landing/CtaSection.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.ctaSection {
padding: 5rem 2rem;
text-align: center;
max-width: 700px;
margin: 0 auto;
}

.ctaSection h2 {
font-family: 'DM Serif Display', serif;
font-size: 38px;
font-weight: 400;
margin-bottom: 1rem;
line-height: 1.15;
color: var(--landing-text);
}

.ctaSection p {
font-size: 16px;
color: var(--landing-muted);
margin-bottom: 2.5rem;
line-height: 1.7;
}

.ctaActions {
display: flex;
gap: 14px;
justify-content: center;
flex-wrap: wrap;
}

@media (max-width: 560px) {
.ctaSection h2 {
font-size: 28px;
}

.ctaActions {
flex-direction: column;
align-items: center;
}
}
26 changes: 26 additions & 0 deletions src/components/landing/CtaSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Button from "./Button";
import styles from "./CtaSection.module.css";

export default function CtaSection() {
return (
<section className={styles.ctaSection} aria-labelledby="cta-heading">
<h2 id="cta-heading">Ready to try EduIDE?</h2>
<p>
Open the IDE in your browser. Or deploy it for your next course in
minutes.
</p>
<div className={styles.ctaActions}>
<Button
variant="primary"
to="https://theia.artemis.cit.tum.de/"
target="_blank"
>
Get Started
</Button>
<Button variant="secondary" to="/student/intro">
Read the Docs
</Button>
</div>
</section>
);
}
119 changes: 119 additions & 0 deletions src/components/landing/FeatureShowcase.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
.section {
padding: 5rem 2rem;
max-width: 1100px;
margin: 0 auto;
}

.inner {
display: grid;
grid-template-columns: 1fr 1.5fr;
gap: 4rem;
align-items: center;
}

.inner.reverse {
grid-template-columns: 1.5fr 1fr;
}

.inner.reverse .textCol {
order: 2;
}

.inner.reverse .mediaCol {
order: 1;
}

.textCol {
display: flex;
flex-direction: column;
gap: 0;
}

.label {
font-size: 12px;
font-weight: 500;
letter-spacing: 0.09em;
text-transform: uppercase;
color: var(--landing-teal);
margin-bottom: 0.75rem;
}

.textCol h2 {
font-size: 2rem;
font-weight: 600;
line-height: 1.15;
letter-spacing: -0.03em;
color: var(--landing-text);
margin-bottom: 1rem;
}

.textCol p {
font-size: 16px;
color: var(--landing-muted);
line-height: 1.75;
margin-bottom: 1.5rem;
}

.link {
font-size: 15px;
font-weight: 500;
color: var(--landing-teal);
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 5px;
}

.link:hover {
text-decoration: underline;
}

.mediaCol {
border-radius: 16px;
overflow: hidden;
background: linear-gradient(135deg, #3b82f6 0%, #6366f1 100%);
aspect-ratio: 16 / 10;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.12);
}

.mediaCol img,
.mediaCol video {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
border-radius: 16px;
}

.gifPlaceholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
}

.gifPlaceholder svg {
opacity: 0.5;
}

@media (max-width: 820px) {
.inner,
.inner.reverse {
grid-template-columns: 1fr;
gap: 2rem;
}

.inner.reverse .textCol {
order: 0;
}

.inner.reverse .mediaCol {
order: 0;
}
}
89 changes: 89 additions & 0 deletions src/components/landing/FeatureShowcase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from 'react';
import { useScrollReveal } from '../../hooks/useScrollReveal';
import styles from './FeatureShowcase.module.css';

interface FeatureShowcaseProps {
label: string;
title: string;
description: string;
linkText?: string;
linkHref?: string;
mediaSrc?: string;
mediaAlt?: string;
mediaType?: 'img' | 'gif' | 'video';
reverse?: boolean;
accentColor?: string;
}

function MediaArea({
mediaSrc,
mediaAlt,
mediaType,
accentColor,
}: Pick<FeatureShowcaseProps, 'mediaSrc' | 'mediaAlt' | 'mediaType' | 'accentColor'>) {
const style = accentColor ? { background: accentColor } : undefined;

if (mediaSrc) {
return (
<div className={styles.mediaCol} style={style}>
{mediaType === 'video' ? (
<video src={mediaSrc} autoPlay loop muted playsInline aria-label={mediaAlt} />
) : (
<img src={mediaSrc} alt={mediaAlt ?? ''} />
)}
</div>
);
}

return (
<div className={styles.mediaCol} style={style}>
<div className={styles.gifPlaceholder}>
{/* Codicon: device-desktop (16x16) */}
<svg width="48" height="48" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M15 2H1L0 3v8l1 1h5v1H4v1h8v-1H9v-1h5l1-1V3l-1-1zM1 11V3h14v8H1z"/>
</svg>
<span>GIF coming soon</span>
</div>
</div>
);
}

const FeatureShowcase = React.memo<FeatureShowcaseProps>(function FeatureShowcase({
label,
title,
description,
linkText,
linkHref,
mediaSrc,
mediaAlt,
mediaType = 'img',
reverse = false,
accentColor,
}) {
const sectionRef = useScrollReveal<HTMLElement>();

return (
<section ref={sectionRef} className={styles.section}>
<div className={`${styles.inner} ${reverse ? styles.reverse : ''}`}>
<div className={styles.textCol}>
<div className={styles.label}>{label}</div>
<h2>{title}</h2>
<p>{description}</p>
{linkText && linkHref && (
<a className={styles.link} href={linkHref}>
{linkText} →
</a>
)}
</div>
<MediaArea
mediaSrc={mediaSrc}
mediaAlt={mediaAlt}
mediaType={mediaType}
accentColor={accentColor}
/>
</div>
</section>
);
});

export default FeatureShowcase;
Loading
Loading