initial commit

This commit is contained in:
baldeau 2024-10-13 22:27:33 +02:00
commit dd2a768e72
26 changed files with 2016 additions and 0 deletions

34
.gitignore vendored Normal file
View File

@ -0,0 +1,34 @@
# Files and directories created by pub.
**/doc/api/
.dart_tool/
.packages
# Conventional directory for build output.
/build/
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter related
.flutter-plugins
.flutter-plugins-dependencies

51
README.md Normal file
View File

@ -0,0 +1,51 @@
# hsrw_campus_website
The app landing page for the HSRW Campus App.
![App Screenshot 1](./screenshot/one.png)
![App Screenshot 2](./screenshot/two.png)
## Requirements
Install jaspr:
```bash
dart pub global activate jaspr_cli
```
Install `tailwindcss`:
- macOS
```bash
brew install tailwindcss
```
- Windows
```powershell
winget install --id=TailwindLabs.TailwindCSS -e
```
- Linux (manual installation)
```bash
curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-<your-platform>
chmod +x tailwindcss-<your-platform>
# e.g. /usr/local/bin on unix based systems (linux, macos)
mv tailwindcss-<your-platform> /usr/local/bin/tailwindcss
```
## Running the project
Run your project using `jaspr serve`.
The development server will be available on `http://localhost:8080`.
## Building the project
Currently there is a build error and the `style.css` file is not generated correctly. This will be fixed in the future, please use the `jaspr serve` command to run the project.
Build your project using `jaspr build`.
The output will be located inside the `build/jaspr/` directory.

42
analysis_options.yaml Normal file
View File

@ -0,0 +1,42 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
analyzer:
# Jaspr has a custom lint package 'jaspr_lints', which needs the 'custom_lint' analyzer plugin.
#
# Unfortunately, running 'dart analyze' does not pick up the custom lints. Instead, you need to
# run a separate command for this: `jaspr analyze`
plugins:
- custom_lint
# exclude:
# - path/to/excluded/files/**
# Uncomment the following section to enable or disable additional rules.
# linter:
# rules:
# camel_case_types: true
# For controlling Jaspr specific lint rules, we need a slightly different config.
# custom_lint:
# rules:
# prefer_html_methods: false
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

67
lib/app.dart Normal file
View File

@ -0,0 +1,67 @@
import 'package:hsrw_campus_website/pages/landing.dart';
import 'package:hsrw_campus_website/pages/tos.dart';
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_router/jaspr_router.dart';
import 'components/header.dart';
import 'components/footer.dart';
import 'pages/privacy.dart';
import 'state/theme_state.dart';
class App extends StatefulComponent {
const App({super.key});
@override
State<App> createState() => _AppState();
}
class _AppState extends State<App> {
bool _isDarkMode = true;
void _toggleTheme(bool value) {
setState(() {
_isDarkMode = value;
});
}
@override
Iterable<Component> build(BuildContext context) sync* {
yield ThemeState(
isDarkMode: _isDarkMode,
toggleTheme: _toggleTheme,
child: div(
classes:
'min-h-screen ${_isDarkMode ? 'bg-gray-900 text-white' : 'bg-gray-100 text-gray-900'} flex flex-col',
[
Router(routes: [
ShellRoute(
builder: (context, state, child) => Fragment(children: [
div(classes: 'fixed top-0 left-0 right-0 z-10', [
const Header(),
]),
div(classes: 'flex-grow pt-24', [
child,
]),
const Footer(),
]),
routes: [
Route(
path: '/',
title: 'Home',
builder: (context, state) => const LandingPage()),
Route(
path: '/privacy',
title: 'Privacy Policy',
builder: (context, state) => const PrivacyPolicyPage()),
Route(
path: '/tos',
title: 'Terms of Service',
builder: (context, state) => const TermsOfServicePage()),
],
),
]),
],
),
);
}
}

View File

@ -0,0 +1,55 @@
import 'package:jaspr/jaspr.dart';
import '../constants/theme.dart';
class Counter extends StatefulComponent {
const Counter({super.key});
@override
State<Counter> createState() => CounterState();
}
class CounterState extends State<Counter> {
int count = 0;
@override
Iterable<Component> build(BuildContext context) sync* {
yield div(classes: 'counter', [
button(
onClick: () {
setState(() => count--);
},
[text('-')],
),
span([text('$count')]),
button(
onClick: () {
setState(() => count++);
},
[text('+')],
),
]);
}
@css
static final styles = [
css('.counter', [
css('&').flexbox(alignItems: AlignItems.center).box(
padding: EdgeInsets.symmetric(vertical: 10.px),
border: Border.symmetric(vertical: BorderSide.solid(color: primaryColor, width: 1.px)),
),
css('button', [
css('&')
.text(fontSize: 2.rem)
.box(width: 2.em, height: 2.em, border: Border.unset, cursor: Cursor.pointer)
.box(radius: BorderRadius.all(Radius.circular(2.em)))
.flexbox(justifyContent: JustifyContent.center, alignItems: AlignItems.center)
.background(color: Colors.transparent),
css('&:hover').background(color: const Color.hex('#0001')),
]),
css('span') //
.box(padding: EdgeInsets.symmetric(horizontal: 2.rem), boxSizing: BoxSizing.borderBox, minWidth: 2.5.em)
.text(color: primaryColor, fontSize: 4.rem, align: TextAlign.center),
]),
];
}

View File

@ -0,0 +1,67 @@
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_router/jaspr_router.dart';
import '../state/theme_state.dart';
import '../config/app_config.dart';
class Footer extends StatelessComponent {
const Footer({super.key});
@override
Iterable<Component> build(BuildContext context) sync* {
var themeState = ThemeState.of(context);
yield footer(
classes: themeState.isDarkMode
? 'bg-gray-800 text-gray-300'
: 'bg-gray-200 text-gray-700',
[
div(
classes: 'max-w-7xl mx-auto py-8 px-4 sm:px-6 lg:px-8',
[
div(
classes: 'flex flex-col md:flex-row justify-between items-center',
[
// Logo and company name
div(
classes: 'flex items-center mb-4 md:mb-0',
[
img(
src: AppConfig.appLogo,
alt: 'Company logo',
classes: 'h-8 w-8 mr-2',
),
span(
classes:
'font-semibold text-lg ${themeState.isDarkMode ? 'text-white' : 'text-gray-900'}',
[text(AppConfig.appName)],
),
],
),
// Navigation links
nav(
classes: 'flex flex-wrap justify-center space-x-4',
[
for (var entry in AppConfig.navLinks.entries)
Link(to: entry.value, child: text(entry.key)),
],
),
],
),
div(
classes:
'mt-8 border-t ${themeState.isDarkMode ? 'border-gray-700' : 'border-gray-300'} pt-8 text-center',
[
p(
classes: themeState.isDarkMode
? 'text-sm text-gray-400'
: 'text-sm text-gray-500',
[text(AppConfig.footerText)],
),
],
),
],
),
],
);
}
}

146
lib/components/header.dart Normal file
View File

@ -0,0 +1,146 @@
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_router/jaspr_router.dart';
import '../state/theme_state.dart';
import '../config/app_config.dart';
class Header extends StatefulComponent {
const Header({super.key});
@override
State<Header> createState() => _HeaderState();
}
class _HeaderState extends State<Header> {
bool _isMenuOpen = false;
void _toggleMenu() {
setState(() {
_isMenuOpen = !_isMenuOpen;
});
}
@override
Iterable<Component> build(BuildContext context) sync* {
var activePath = RouteState.of(context).location;
var themeState = ThemeState.of(context);
yield header(
classes: themeState.isDarkMode
? 'bg-gray-800 shadow-md'
: 'bg-white shadow-md',
[
div(
classes: 'max-w-7xl mx-auto px-4 sm:px-6 lg:px-8',
[
div(
classes: 'flex justify-between items-center h-16',
[
// Logo
Link(
to: '/',
child: div(
classes: 'flex items-center',
[
img(
src: AppConfig.appLogo,
alt: 'Logo',
classes: 'h-8 w-8 mr-2',
),
span(
classes:
'font-bold text-xl ${themeState.isDarkMode ? 'text-white' : 'text-gray-900'}',
[text(AppConfig.appName)],
),
],
),
),
// Mobile menu button
div(
classes: 'md:hidden',
[
button(
classes:
'${themeState.isDarkMode ? 'text-gray-300 hover:text-white' : 'text-gray-600 hover:text-gray-900'} focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500',
events: {'click': (e) => _toggleMenu()},
[
span(classes: 'sr-only', [text('Open main menu')]),
// Hamburger icon
svg(
classes: 'h-6 w-6',
attributes: {
'fill': 'none',
'viewBox': '0 0 24 24',
'stroke': 'currentColor',
'aria-hidden': 'true',
},
[
DomComponent(
tag: 'path',
attributes: {
'stroke-linecap': 'round',
'stroke-linejoin': 'round',
'stroke-width': '2',
'd': 'M4 6h16M4 12h16M4 18h16',
},
),
],
),
],
),
],
),
// Desktop navigation
nav(
classes: 'hidden md:flex space-x-4',
[
for (var entry in AppConfig.navLinks.entries)
Link(
to: entry.value,
child: div(
classes:
'px-3 py-2 rounded-md text-sm font-medium ${activePath == entry.value ? (themeState.isDarkMode ? 'bg-gray-900 text-white' : 'bg-gray-200 text-gray-900') : (themeState.isDarkMode ? 'text-gray-300 hover:bg-gray-700 hover:text-white' : 'text-gray-700 hover:bg-gray-200 hover:text-gray-900')}',
[text(entry.key)],
),
),
],
),
// Theme toggle button
button(
classes:
'ml-4 p-2 rounded-md ${themeState.isDarkMode ? 'bg-gray-700 text-yellow-400' : 'bg-gray-200 text-gray-800'}',
events: {
'click': (e) =>
themeState.toggleTheme(!themeState.isDarkMode),
},
[
text(themeState.isDarkMode ? '☀️' : '🌙'),
],
),
],
),
],
),
// Mobile navigation
div(
classes: 'md:hidden ${_isMenuOpen ? '' : 'hidden'}',
[
div(
classes: 'px-2 pt-2 pb-3 space-y-1 sm:px-3',
[
for (var entry in AppConfig.navLinks.entries)
Link(
to: entry.value,
child: div(
classes:
'block px-3 py-2 rounded-md text-base font-medium ${activePath == entry.value ? (themeState.isDarkMode ? 'bg-gray-900 text-white' : 'bg-gray-200 text-gray-900') : (themeState.isDarkMode ? 'text-gray-300 hover:bg-gray-700 hover:text-white' : 'text-gray-700 hover:bg-gray-200 hover:text-gray-900')}',
[text(entry.key)],
),
),
],
),
],
),
],
);
}
}

131
lib/config/app_config.dart Normal file
View File

@ -0,0 +1,131 @@
class AppConfig {
static const String appName = 'HSRW Campus App';
static const String appLogo = 'https://placehold.co/40x40?text=Logo';
static const Map<String, String> navLinks = {
'Home': '/',
'Privacy': '/privacy',
'Terms': '/tos',
};
static const String heroTitle = 'HSRW Campus App';
static const String heroSubtitle =
'Lorem ipsum, dolor sit amet consectetur adipisicing elit.';
static const String heroImage =
'https://placehold.co/640x1386?text=App+Screenshot';
static const String featuresTitle = 'Features';
static const String featuresSubtitle = 'Everything you need';
static const String featuresDescription =
'Lorem ipsum dolor sit amet consect adipisicing elit. Possimus magnam voluptatum cupiditate veritatis in accusamus quisquam.';
static const List<Feature> features = [
Feature(
title: 'Feature 1',
description:
'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Maiores impedit perferendis suscipit eaque, iste dolor cupiditate blanditiis ratione.',
icon: '🚀',
),
Feature(
title: 'Feature 2',
description:
'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Maiores impedit perferendis suscipit eaque, iste dolor cupiditate blanditiis ratione.',
icon: '💡',
),
Feature(
title: 'Feature 3',
description:
'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Maiores impedit perferendis suscipit eaque, iste dolor cupiditate blanditiis ratione.',
icon: '🔧',
),
Feature(
title: 'Feature 4',
description:
'Lorem ipsum, dolor sit amet consectetur adipisicing elit. Maiores impedit perferendis suscipit eaque, iste dolor cupiditate blanditiis ratione.',
icon: '📊',
),
];
static const String ctaTitle = 'Ready to dive in?';
static const String ctaSubtitle = 'Download now and start your journey.';
static const String ctaDescription =
'Available on iOS and Android. Download now and experience the future.';
static const String appStoreLink = '#';
static const String appStoreImage =
'https://placehold.co/120x40?text=App+Store';
static const String playStoreLink = '#';
static const String playStoreImage =
'https://placehold.co/135x40?text=Google+Play';
static const String footerText =
'© 2023 HSRW Campus App. All rights reserved.';
// Privacy Policy
static const String privacyTitle = 'Privacy Policy';
static const String privacyLastUpdated = 'Last updated: May 1, 2023';
static const List<PrivacySection> privacySections = [
PrivacySection(
title: 'Introduction',
content:
'This Privacy Policy describes how Your Indie App ("we", "our", or "us") collects, uses, and shares your personal information when you use our mobile application.',
),
PrivacySection(
title: 'Information We Collect',
content:
'We collect information that you provide directly to us, such as when you create an account, use our services, or contact us for support. This may include your name, email address, and usage data.',
),
PrivacySection(
title: 'How We Use Your Information',
content:
'We use the information we collect to provide, maintain, and improve our services, to communicate with you, and to comply with legal obligations.',
),
];
// Terms of Service
static const String tosTitle = 'Terms of Service';
static const String tosLastUpdated = 'Last updated: May 1, 2023';
static const List<TOSSection> tosSections = [
TOSSection(
title: 'Acceptance of Terms',
content:
'By accessing or using Your Indie App, you agree to be bound by these Terms of Service and all applicable laws and regulations.',
),
TOSSection(
title: 'Use of the Service',
content:
'You may use our service for your personal, non-commercial use only. You must not use the service for any illegal or unauthorized purpose.',
),
TOSSection(
title: 'User Accounts',
content:
'You are responsible for maintaining the confidentiality of your account and password. You agree to accept responsibility for all activities that occur under your account.',
),
];
// Landing page layout configuration
static const bool useAlternativeFeatureLayout =
false; // Set to true to use the new layout
}
class Feature {
final String title;
final String description;
final String icon;
const Feature(
{required this.title, required this.description, required this.icon});
}
class PrivacySection {
final String title;
final String content;
const PrivacySection({required this.title, required this.content});
}
class TOSSection {
final String title;
final String content;
const TOSSection({required this.title, required this.content});
}

5
lib/constants/theme.dart Normal file
View File

@ -0,0 +1,5 @@
import 'package:jaspr/jaspr.dart';
// As your css styles are defined using just Dart, you can simply
// use global variables or methods for common things like colors.
const primaryColor = Color.hex('#01589B');

28
lib/jaspr_options.dart Normal file
View File

@ -0,0 +1,28 @@
// GENERATED FILE, DO NOT MODIFY
// Generated with jaspr_builder
import 'package:jaspr/jaspr.dart';
import 'package:hsrw_campus_website/pages/home.dart' as prefix0;
/// Default [JasprOptions] for use with your jaspr project.
///
/// Use this to initialize jaspr **before** calling [runApp].
///
/// Example:
/// ```dart
/// import 'jaspr_options.dart';
///
/// void main() {
/// Jaspr.initializeApp(
/// options: defaultJasprOptions,
/// );
///
/// runApp(...);
/// }
/// ```
final defaultJasprOptions = JasprOptions(
clients: {
prefix0.Home: ClientTarget<prefix0.Home>('pages/home'),
},
styles: () => [],
);

33
lib/main.dart Normal file
View File

@ -0,0 +1,33 @@
// The entrypoint for the **server** environment.
//
// The [main] method will only be executed on the server during pre-rendering.
// To run code on the client, use the @client annotation.
// Server-specific jaspr import.
import 'package:jaspr/server.dart';
// Imports the [App] component.
import 'app.dart';
// This file is generated automatically by Jaspr, do not remove or edit.
import 'jaspr_options.dart';
void main() {
// Initializes the server environment with the generated default options.
Jaspr.initializeApp(
options: defaultJasprOptions,
);
// Starts the app.
//
// [Document] renders the root document structure (<html>, <head> and <body>)
// with the provided parameters and components.
runApp(Document(
title: 'hsrw_campus_website',
head: [
// Link the styles.css file, this will be generated by the tailwind integration.
link(href: 'styles.css', rel: 'stylesheet'),
],
body: App(),
));
}

40
lib/pages/about.dart Normal file
View File

@ -0,0 +1,40 @@
import 'package:jaspr/jaspr.dart';
class About extends StatelessComponent {
const About({super.key});
@override
Iterable<Component> build(BuildContext context) sync* {
yield section([
ol([
li([
h3([text('📖 Documentation')]),
text('Jaspr\'s '),
a(
href: 'https://docs.page/schultek/jaspr',
[text('official documentation')]),
text(' provides you with all information you need to get started.'),
]),
li([
h3([text('💬 Community')]),
text('Got stuck? Ask your question on the official '),
a(href: 'https://docs.page/schultek/jaspr', [text('Discord server')]),
text(' for the Jaspr community.'),
]),
li([
h3([text('📦 Ecosystem')]),
text(
'Get official packages and integrations for your project like jaspr_router, jaspr_tailwind or jaspr_riverpod. Find packages built for Jaspr on pub.dev using the '),
a(href: 'https://pub.dev/packages?q=topic%3Ajaspr', [text('#jaspr')]),
text(' topic, or publish your own.'),
]),
li([
h3([text('💙 Support Jaspr')]),
text('If you like Jaspr, consider starring us on '),
a(href: 'https://github.com/schultek/jaspr', [text('Github')]),
text(' and tell your friends.'),
]),
]),
]);
}
}

44
lib/pages/home.dart Normal file
View File

@ -0,0 +1,44 @@
import 'package:jaspr/jaspr.dart';
import '../components/counter.dart';
// By using the @client annotation this component will be automatically compiled to javascript and mounted
// on the client. Therefore:
// - this file and any imported file must be compilable for both server and client environments.
// - this component and any child components will be built once on the server during pre-rendering and then
// again on the client during normal rendering.
@client
class Home extends StatefulComponent {
const Home({super.key});
@override
State<Home> createState() => HomeState();
}
class HomeState extends State<Home> {
@override
void initState() {
super.initState();
// Run code depending on the rendering environment.
if (kIsWeb) {
print("Hello client");
// When using @client components there is no default `main()` function on the client where you would normally
// run any client-side initialization logic. Instead you can put it here, considering this component is only
// mounted once at the root of your client-side component tree.
} else {
print("Hello server");
}
}
@override
Iterable<Component> build(BuildContext context) sync* {
yield section([
img(src: 'images/logo.png', width: 80),
h1([text('Welcome')]),
p([text('You successfully create a new Jaspr site.')]),
div(styles: Styles.box(height: 100.px), []),
const Counter(),
]);
}
}

201
lib/pages/landing.dart Normal file
View File

@ -0,0 +1,201 @@
import 'package:jaspr/jaspr.dart';
import '../state/theme_state.dart';
import '../config/app_config.dart';
class LandingPage extends StatelessComponent {
const LandingPage({super.key});
@override
Iterable<Component> build(BuildContext context) sync* {
var themeState = ThemeState.of(context);
yield div(classes: 'min-h-screen flex flex-col', [
// Hero section
section(
classes:
'flex-1 flex flex-col justify-center items-center text-center px-4 sm:px-6 lg:px-8 ${AppConfig.useAlternativeFeatureLayout ? 'pb-16' : ''}',
[
h1(
classes:
'text-4xl sm:text-5xl md:text-6xl font-extrabold ${themeState.isDarkMode ? 'text-white' : 'text-gray-900'}',
[text(AppConfig.heroTitle)],
),
p(
classes:
'mt-3 max-w-md mx-auto text-xl ${themeState.isDarkMode ? 'text-gray-300' : 'text-gray-600'} sm:text-2xl md:mt-5 md:max-w-3xl',
[text(AppConfig.heroSubtitle)],
),
// App showcase (iPhone frame) for original layout
if (!AppConfig.useAlternativeFeatureLayout)
div(classes: 'mt-16 max-w-lg mx-auto', [
div(classes: 'iphone-mask', [
img(
src: AppConfig.heroImage,
alt: 'App Screenshot',
classes: 'w-full h-auto'),
]),
]),
],
),
// Features section with conditional layout
AppConfig.useAlternativeFeatureLayout
? _buildAlternativeFeatureSection(themeState)
: _buildOriginalFeatureSection(themeState),
// CTA section
section(
classes:
'bg-indigo-700 ${AppConfig.useAlternativeFeatureLayout ? 'mt-16' : ''}',
[
div(
classes:
'max-w-2xl mx-auto text-center py-16 px-4 sm:py-20 sm:px-6 lg:px-8',
[
h2(
classes: 'text-3xl font-extrabold text-white sm:text-4xl',
[
span(classes: 'block', [text(AppConfig.ctaTitle)]),
span(classes: 'block', [text(AppConfig.ctaSubtitle)]),
]),
p(
classes: 'mt-4 text-lg leading-6 text-indigo-200',
[text(AppConfig.ctaDescription)]),
div(classes: 'mt-8 flex justify-center space-x-4', [
a(
classes:
'inline-flex items-center px-5 py-3 border border-transparent text-base font-medium rounded-md text-indigo-600 bg-white hover:bg-indigo-50',
href: AppConfig.appStoreLink,
[
img(
src: AppConfig.appStoreImage,
alt: 'Download on the App Store',
classes: 'h-10'),
]),
a(
classes:
'inline-flex items-center px-5 py-3 border border-transparent text-base font-medium rounded-md text-indigo-600 bg-white hover:bg-indigo-50',
href: AppConfig.playStoreLink,
[
img(
src: AppConfig.playStoreImage,
alt: 'Get it on Google Play',
classes: 'h-10'),
]),
]),
]),
]),
]);
}
Component _buildOriginalFeatureSection(ThemeState themeState) {
return section(
classes: 'py-12 ${themeState.isDarkMode ? 'bg-gray-800' : 'bg-gray-100'}',
[
div(classes: 'max-w-7xl mx-auto px-4 sm:px-6 lg:px-8', [
div(classes: 'lg:text-center', [
h2(
classes:
'text-base text-indigo-400 font-semibold tracking-wide uppercase',
[text(AppConfig.featuresTitle)],
),
p(
classes:
'mt-2 text-3xl leading-8 font-extrabold tracking-tight ${themeState.isDarkMode ? 'text-white' : 'text-gray-900'} sm:text-4xl',
[text(AppConfig.featuresSubtitle)],
),
p(
classes:
'mt-4 max-w-2xl text-xl ${themeState.isDarkMode ? 'text-gray-300' : 'text-gray-500'} lg:mx-auto',
[text(AppConfig.featuresDescription)],
),
]),
div(classes: 'mt-10', [
div(
classes:
'space-y-10 md:space-y-0 md:grid md:grid-cols-2 md:gap-x-8 md:gap-y-10',
[
for (var feature in AppConfig.features)
_buildFeatureItem(feature, themeState),
],
),
]),
]),
],
);
}
Component _buildAlternativeFeatureSection(ThemeState themeState) {
return section(
classes: 'py-16 ${themeState.isDarkMode ? 'bg-gray-800' : 'bg-gray-100'}',
[
div(classes: 'max-w-7xl mx-auto px-4 sm:px-6 lg:px-8', [
div(classes: 'lg:text-center mb-16', [
h2(
classes:
'text-base text-indigo-400 font-semibold tracking-wide uppercase',
[text(AppConfig.featuresTitle)],
),
p(
classes:
'mt-2 text-3xl leading-8 font-extrabold tracking-tight ${themeState.isDarkMode ? 'text-white' : 'text-gray-900'} sm:text-4xl',
[text(AppConfig.featuresSubtitle)],
),
p(
classes:
'mt-4 max-w-2xl text-xl ${themeState.isDarkMode ? 'text-gray-300' : 'text-gray-500'} lg:mx-auto',
[text(AppConfig.featuresDescription)],
),
]),
div(classes: 'flex flex-col lg:flex-row items-center', [
// App screenshot with iPhone frame
div(classes: 'lg:w-1/2 mb-10 lg:mb-0', [
div(classes: 'max-w-md mx-auto', [
div(classes: 'iphone-mask', [
img(
src: AppConfig.heroImage,
alt: 'App Screenshot',
classes: 'w-full h-auto',
),
]),
]),
]),
// Features list
div(classes: 'lg:w-1/2 lg:pl-12', [
div(classes: 'space-y-10', [
for (var feature in AppConfig.features)
_buildFeatureItem(feature, themeState),
]),
]),
]),
]),
],
);
}
Component _buildFeatureItem(Feature feature, ThemeState themeState) {
return div(classes: 'relative', [
div([
div(
classes:
'absolute flex items-center justify-center h-12 w-12 rounded-md bg-indigo-500 text-white',
[
span(classes: 'text-2xl', [text(feature.icon)]),
],
),
div(classes: 'ml-16', [
h3(
classes:
'text-lg leading-6 font-medium ${themeState.isDarkMode ? 'text-white' : 'text-gray-900'}',
[text(feature.title)],
),
p(
classes:
'mt-2 text-base ${themeState.isDarkMode ? 'text-gray-300' : 'text-gray-500'}',
[text(feature.description)],
),
]),
]),
]);
}
}

46
lib/pages/privacy.dart Normal file
View File

@ -0,0 +1,46 @@
import 'package:jaspr/jaspr.dart';
import '../state/theme_state.dart';
import '../config/app_config.dart';
class PrivacyPolicyPage extends StatelessComponent {
const PrivacyPolicyPage({super.key});
@override
Iterable<Component> build(BuildContext context) sync* {
var themeState = ThemeState.of(context);
yield div(
classes: 'max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12',
[
h1(
classes:
'text-3xl font-bold ${themeState.isDarkMode ? 'text-white' : 'text-gray-900'}',
[text(AppConfig.privacyTitle)],
),
p(
classes:
'mt-2 text-sm ${themeState.isDarkMode ? 'text-gray-400' : 'text-gray-600'}',
[text(AppConfig.privacyLastUpdated)],
),
div(
classes: 'mt-8 space-y-8',
[
for (var section in AppConfig.privacySections)
div([
h2(
classes:
'text-xl font-semibold ${themeState.isDarkMode ? 'text-white' : 'text-gray-900'}',
[text(section.title)],
),
p(
classes:
'mt-2 ${themeState.isDarkMode ? 'text-gray-300' : 'text-gray-600'}',
[text(section.content)],
),
]),
],
),
],
);
}
}

46
lib/pages/tos.dart Normal file
View File

@ -0,0 +1,46 @@
import 'package:jaspr/jaspr.dart';
import '../state/theme_state.dart';
import '../config/app_config.dart';
class TermsOfServicePage extends StatelessComponent {
const TermsOfServicePage({super.key});
@override
Iterable<Component> build(BuildContext context) sync* {
var themeState = ThemeState.of(context);
yield div(
classes: 'max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12',
[
h1(
classes:
'text-3xl font-bold ${themeState.isDarkMode ? 'text-white' : 'text-gray-900'}',
[text(AppConfig.tosTitle)],
),
p(
classes:
'mt-2 text-sm ${themeState.isDarkMode ? 'text-gray-400' : 'text-gray-600'}',
[text(AppConfig.tosLastUpdated)],
),
div(
classes: 'mt-8 space-y-8',
[
for (var section in AppConfig.tosSections)
div([
h2(
classes:
'text-xl font-semibold ${themeState.isDarkMode ? 'text-white' : 'text-gray-900'}',
[text(section.title)],
),
p(
classes:
'mt-2 ${themeState.isDarkMode ? 'text-gray-300' : 'text-gray-600'}',
[text(section.content)],
),
]),
],
),
],
);
}
}

View File

@ -0,0 +1,22 @@
import 'package:jaspr/jaspr.dart';
class ThemeState extends InheritedComponent {
final bool isDarkMode;
final Function(bool) toggleTheme;
const ThemeState({
required this.isDarkMode,
required this.toggleTheme,
required super.child,
super.key,
});
static ThemeState of(BuildContext context) {
return context.dependOnInheritedComponentOfExactType<ThemeState>()!;
}
@override
bool updateShouldNotify(ThemeState oldComponent) {
return isDarkMode != oldComponent.isDarkMode;
}
}

714
pubspec.lock Normal file
View File

@ -0,0 +1,714 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77"
url: "https://pub.dev"
source: hosted
version: "73.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.2"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a"
url: "https://pub.dev"
source: hosted
version: "6.8.0"
analyzer_plugin:
dependency: transitive
description:
name: analyzer_plugin
sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161"
url: "https://pub.dev"
source: hosted
version: "0.11.3"
archive:
dependency: transitive
description:
name: archive
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
url: "https://pub.dev"
source: hosted
version: "3.6.1"
args:
dependency: transitive
description:
name: args
sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
url: "https://pub.dev"
source: hosted
version: "2.5.0"
async:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.11.0"
bazel_worker:
dependency: transitive
description:
name: bazel_worker
sha256: "4eef19cc486c289e4b06c69d0f6f3192e85cc93c25d4d15d02afb205e388d2f0"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
binary_codec:
dependency: transitive
description:
name: binary_codec
sha256: "368144225d749e1e33f2f4628d0c70bffff99b99b1d6c0777b039f8967365b07"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
build:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
build_config:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
build_modules:
dependency: transitive
description:
name: build_modules
sha256: "403ba034d94f1a0f26362fe14fd83e9ff33644f5cbe879982920e3d209650b43"
url: "https://pub.dev"
source: hosted
version: "5.0.9"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
url: "https://pub.dev"
source: hosted
version: "7.3.2"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
url: "https://pub.dev"
source: hosted
version: "8.9.2"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.3"
ci:
dependency: transitive
description:
name: ci
sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13"
url: "https://pub.dev"
source: hosted
version: "0.1.0"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19
url: "https://pub.dev"
source: hosted
version: "0.4.1"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
url: "https://pub.dev"
source: hosted
version: "4.10.0"
collection:
dependency: transitive
description:
name: collection
sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
url: "https://pub.dev"
source: hosted
version: "1.19.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27
url: "https://pub.dev"
source: hosted
version: "3.0.5"
csslib:
dependency: transitive
description:
name: csslib
sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
custom_lint:
dependency: transitive
description:
name: custom_lint
sha256: "4939d89e580c36215e48a7de8fd92f22c79dcc3eb11fda84f3402b3b45aec663"
url: "https://pub.dev"
source: hosted
version: "0.6.5"
custom_lint_builder:
dependency: transitive
description:
name: custom_lint_builder
sha256: d9e5bb63ed52c1d006f5a1828992ba6de124c27a531e8fba0a31afffa81621b3
url: "https://pub.dev"
source: hosted
version: "0.6.5"
custom_lint_core:
dependency: transitive
description:
name: custom_lint_core
sha256: "4ddbbdaa774265de44c97054dcec058a83d9081d071785ece601e348c18c267d"
url: "https://pub.dev"
source: hosted
version: "0.6.5"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
url: "https://pub.dev"
source: hosted
version: "2.3.7"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
freezed_annotation:
dependency: transitive
description:
name: freezed_annotation
sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2
url: "https://pub.dev"
source: hosted
version: "2.4.4"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
hotreloader:
dependency: transitive
description:
name: hotreloader
sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e
url: "https://pub.dev"
source: hosted
version: "4.2.0"
html:
dependency: transitive
description:
name: html
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
url: "https://pub.dev"
source: hosted
version: "0.15.4"
http:
dependency: transitive
description:
name: http
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "1.2.2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "40f592dd352890c3b60fec1b68e786cefb9603e05ff303dbc4dda49b304ecdf4"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
io:
dependency: transitive
description:
name: io
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
url: "https://pub.dev"
source: hosted
version: "1.0.4"
jaspr:
dependency: "direct main"
description:
name: jaspr
sha256: "0e43fa3bfa364ef0f51cf849b20dbad0bf6c4c3c0f1b1adab8a09e2c7730d55c"
url: "https://pub.dev"
source: hosted
version: "0.16.0"
jaspr_builder:
dependency: "direct dev"
description:
name: jaspr_builder
sha256: "383caac3d4ecf025519a4e582660869150453020ccdb6fec763c558046469b4f"
url: "https://pub.dev"
source: hosted
version: "0.16.0"
jaspr_lints:
dependency: "direct dev"
description:
name: jaspr_lints
sha256: "8622080c80d16349268c05ed4cced8ac827002f71c4ea08b6e4fe04d585d85a3"
url: "https://pub.dev"
source: hosted
version: "0.1.2"
jaspr_router:
dependency: "direct main"
description:
name: jaspr_router
sha256: "192d1baf7d722e7c04d4d78676ff97ad2eb920839b1ffee1838d8650d5e1be7a"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
jaspr_tailwind:
dependency: "direct dev"
description:
name: jaspr_tailwind
sha256: "6bc8e5723d0b58816c71044e50b0f580e6518d9b6e6664e52fbbd81efc416705"
url: "https://pub.dev"
source: hosted
version: "0.3.0"
jaspr_web_compilers:
dependency: "direct dev"
description:
name: jaspr_web_compilers
sha256: "068e42fbb89e5a7b7d47849886669b80a0d9e011f76bbfd85f6444d0b80cc4f4"
url: "https://pub.dev"
source: hosted
version: "4.0.10"
js:
dependency: transitive
description:
name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev"
source: hosted
version: "0.7.1"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
lints:
dependency: "direct dev"
description:
name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev"
source: hosted
version: "3.0.0"
logging:
dependency: transitive
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
macros:
dependency: transitive
description:
name: macros
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
url: "https://pub.dev"
source: hosted
version: "0.1.2-main.4"
matcher:
dependency: transitive
description:
name: matcher
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16+1"
meta:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.dev"
source: hosted
version: "1.16.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
package_config:
dependency: transitive
description:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
path:
dependency: transitive
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
protobuf:
dependency: transitive
description:
name: protobuf
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
url: "https://pub.dev"
source: hosted
version: "1.3.0"
rxdart:
dependency: transitive
description:
name: rxdart
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
url: "https://pub.dev"
source: hosted
version: "0.28.0"
scratch_space:
dependency: transitive
description:
name: scratch_space
sha256: "8510fbff458d733a58fc427057d1ac86303b376d609d6e1bc43f240aad9aa445"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_gzip:
dependency: transitive
description:
name: shelf_gzip
sha256: "4f4b793c0f969f348aece1ab4cc05fceba9fea431c1ce76b1bc0fa369cecfc15"
url: "https://pub.dev"
source: hosted
version: "4.1.0"
shelf_proxy:
dependency: transitive
description:
name: shelf_proxy
sha256: a71d2307f4393211930c590c3d2c00630f6c5a7a77edc1ef6436dfd85a6a7ee3
url: "https://pub.dev"
source: hosted
version: "1.0.4"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
url: "https://pub.dev"
source: hosted
version: "1.1.3"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
source_maps:
dependency: transitive
description:
name: source_maps
sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703"
url: "https://pub.dev"
source: hosted
version: "0.10.12"
source_span:
dependency: transitive
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
url: "https://pub.dev"
source: hosted
version: "1.12.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.2"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
url: "https://pub.dev"
source: hosted
version: "0.7.3"
timing:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.2"
uuid:
dependency: transitive
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
url: "https://pub.dev"
source: hosted
version: "4.5.1"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b
url: "https://pub.dev"
source: hosted
version: "14.3.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web:
dependency: transitive
description:
name: web
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.5.0 <3.6.0"

22
pubspec.yaml Normal file
View File

@ -0,0 +1,22 @@
name: hsrw_campus_website
description: A new jaspr project.
version: 0.0.1
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
jaspr: ^0.16.0
jaspr_router: ^0.6.0
dev_dependencies:
build_runner: ^2.4.0
jaspr_web_compilers: ^4.0.10
jaspr_builder: ^0.16.0
jaspr_lints: ^0.1.2
lints: ^3.0.0
jaspr_tailwind: ^0.3.0
jaspr:
mode: static

BIN
screenshot/one.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 KiB

BIN
screenshot/two.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

8
tailwind.config.js Normal file
View File

@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./{lib,web}/**/*.dart'],
theme: {
extend: {},
},
plugins: [],
}

BIN
web/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
web/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

122
web/styles.css Normal file
View File

@ -0,0 +1,122 @@
@import url(https://fonts.googleapis.com/css?family=Roboto);
html,
body {
font-family: 'Roboto', sans-serif;
width: 100%;
min-height: 100vh;
padding: 0;
margin: 0;
--primary-color: #01589B;
}
h1 {
font-size: 4rem;
margin: unset;
}
.main {
height: 100vh;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
.main section {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
header {
display: flex;
justify-content: center;
padding: 1em;
}
header nav {
background-color: var(--primary-color);
height: 3em;
border-radius: 10px;
overflow: clip;
display: flex;
justify-content: space-between;
}
header nav a {
color: white;
font-weight: 700;
text-decoration: none;
padding-left: 2em;
padding-right: 2em;
height: 100%;
display: flex;
align-items: center;
}
header nav a:hover {
background-color: #0005;
}
header nav div.active {
position: relative;
}
header nav div.active::before {
content: "";
display: block;
height: 2px;
border-radius: 1px;
position: absolute;
left: 20px;
bottom: 0.5em;
right: 20px;
background-color: white;
}
.counter {
display: flex;
align-items: center;
padding-top: 10px;
padding-bottom: 10px;
border-top-style: solid;
border-bottom-style: solid;
border-top-color: var(--primary-color);
border-bottom-color: var(--primary-color);
border-top-width: 1px;
border-bottom-width: 1px;
}
.counter button {
font-size: 2rem;
width: 2em;
height: 2em;
border: unset;
cursor: pointer;
border-radius: 2em;
display: flex;
justify-content: center;
align-items: center;
background-color: transparent;
}
.counter button:hover {
background-color: #0001;
}
.counter span {
padding-left: 2rem;
padding-right: 2rem;
box-sizing: border-box;
min-width: 2.5em;
color: var(--primary-color);
font-size: 4rem;
text-align: center;
}
ol {
max-width: 500px;
}

92
web/styles.tw.css Normal file
View File

@ -0,0 +1,92 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
body {
@apply bg-gray-900 text-white;
}
}
@layer components {
.btn {
@apply px-4 py-2 rounded-lg font-semibold transition-colors duration-200;
}
.btn-primary {
@apply bg-indigo-600 hover:bg-indigo-700 text-white;
}
.btn-secondary {
@apply bg-gray-700 hover:bg-gray-600 text-white;
}
.pixel-mask {
position: relative;
width: 310px;
height: 680px;
margin: 0 auto;
overflow: hidden;
border-radius: 30px;
box-shadow: 0 0 0 12px #2b2b2b, 0 0 0 14px #1e1e1e, 0 0 0 22px #101010;
background-color: #000;
}
.iphone-mask {
position: relative;
width: 300px;
height: 650px;
margin: 0 auto;
overflow: hidden;
border-radius: 40px;
box-shadow: 0 0 0 11px #1f1f1f, 0 0 0 13px #191919, 0 0 0 20px #111;
}
.iphone-mask::before {
content: "";
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
height: 30px;
width: 60%;
background-color: #1f1f1f;
border-bottom-left-radius: 20px;
border-bottom-right-radius: 20px;
z-index: 10;
}
.iphone-mask img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
@layer utilities {
.transition-all {
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-300 {
transition-duration: 300ms;
}
@variants responsive {
/* Hide scrollbar for Chrome, Safari and Opera */
.no-scrollbar::-webkit-scrollbar {
display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
.no-scrollbar {
-ms-overflow-style: none;
/* IE and Edge */
scrollbar-width: none;
/* Firefox */
}
}
}