Spaces:
Sleeping
Sleeping
File size: 14,913 Bytes
036b3a6 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
from fasthtml.common import *
from home_components import *
from content import *
_blank = dict(target="_blank", rel="noopener noreferrer")
description = 'Modern web applications in pure Python'
def benefit(title, content):
return Div(
H3(title, cls=f"text-black heading-3"),
P(content, cls=f"l-body mt-6 lg:mt-6"),
cls="w-full p-6 bg-soft-yellow rounded-2xl xl:p-12 lg:h-[22rem] lg:w-[26rem]")
def faq_item(question, answer, id):
return accordion(
id=id, question=question, answer=answer,
question_cls="text-black s-body",
answer_cls="s-body text-black/80 col-span-full",
container_cls=f"{col} justify-between bg-soft-blue rounded-[1.25rem] {bnset}"
)
def hero_section():
return (
Header(
Nav(
A(
Img(src='/assets/logo.svg', alt='FastHTML', width='105', height='24'),
href='#'),
A('Read docs', href='/docs', **_blank,
cls=f'bg-black text-white py-2 px-4 s-body rounded-[62.5rem] hover:bg-black/80 transition-colors duration-300 px-4 py-1 h-10 {center} justify-center'),
cls=f'py-2 px-4 {between} items-center rounded-full w-full max-w-[400px] bg-white/50 backdrop-blur-lg border border-white/20'),
cls=f'fixed top-0 w-full left-0 p-4 {center} justify-center z-50'),
Section(
Div(
File('assets/hero-shapes.svg'),
cls='absolute z-0 lg:-top-[15%] top-0 left-1/2 -translate-x-1/2 grid grid-cols-1 grid-rows-1 w-[120%] aspect-square max-w-[2048px] min-w-[900px]'),
Div(
Div(cls='lg:flex-1 max-lg:basis-[152px]'),
Div(
H1(description, cls='heading-1 max-w-3xl'),
P('Built on solid web foundations, not the latest fads - with\nFastHTML you can get started on anything from simple dashboards to\nscalable web applications in minutes.',
cls='m-body max-w-[40rem] text-center'),
cls=f'flex-1 {col} items-center justify-center gap-6 text-center w-full text-black'
),
Div(
A('Learn more', href='https://fastht.ml/about', **_blank,
cls=f'{bnset} m-body px-4 py-1 rounded-full bg-black hover:bg-black/80 transition-colors duration-300 text-white h-[76px] w-full max-w-[350px] flex items-center justify-center'),
video_button('Watch intro', '/assets/thumb.png', '7min 30sec', "QqZUzkPcU7A?si=lTtHuMT5HPC66-49"),
cls=f'flex-1 {center} justify-center content-center flex-wrap lg:gap-6 gap-4 m-body'),
video_player('Try now'),
cls=f'{col} flex-1 relative px-4 lg:px-16'),
cls=f'{col} relative w-full h-screen max-h-[1024px] min-h-[720px] overflow-hidden bg-grey')
)
def code_display(file_name, code_snippet, snippet_id):
return Div(
Div(
Div(
P(file_name, cls=f"xs-mono-body {center}"),
Button(
Div(
Img(cls="button-content w-4 h-4", src="assets/icons/copy-icon.svg", alt="Copy"),
Span("Copied!", cls=f"absolute bg-inherit rounded-l-[0.5rem] {center} py-2 h-full right-0 copied-text overflow-hidden w-0 peer-clicked:w-fit s-body text-white/80 transition-transform transition-all duration-600 ease-out"),
cls=f"relative bg-black/20 rounded-[0.5rem] {center} p-2 h-8 w-fit"),
cls="copy-button"),
cls=f"w-full {between}"),
cls="bg-black/20 text-white/80 w-full max-w-2xl lg:max-w-xl p-4 rounded-2xl"),
Pre(
Code(code_snippet, cls="python w-full mono-body"),
id=snippet_id,
cls="code-snippet relative max-h-[25rem] overflow-hidden hide-scrollbar"),
Div(cls="absolute code-fade-out bottom-0 left-0 right-0 h-16 bg-gradient-to-b from-transparent to-[#3a2234] pointer-events-none"),
cls=f"relative {col} gap-6 lg:max-w-[45rem] w-full overflow-hidden"
)
def code_demo(title, file_name, code_snippet, demo_content, is_active=False):
snippet_id = f"{title.lower().replace(' ', '-')}-code-snippet"
demo_cls = f"{center} my-11 p-4 flex-none whitespace-normal justify-center h-96 rounded-3xl bg-soft-purple lg:p-8 w-full max-w-2xl lg:max-w-md lg:mx-28 lg:my-8 overflow-y-auto {'lg:items-start hide-scrollbar' if snippet_id == 'reusable-code-snippet' else ''}"
return Div(
code_display(file_name, code_snippet, snippet_id),
Div(demo_content, cls=demo_cls),
aria_labelledby=f"tab-{title.lower().replace(' ', '-')}", role="tabpanel",
cls=f"code-container pt-8 lg:pt-16 tab-panel relative hide-scrollbar toggle-element {col} lg:flex-row lg:justify-between overflow-hidden w-full lg:max-w-[1440px] xl:mx-auto {'hidden' if not is_active else ''}",
id=f"{title.lower().replace(' ', '-')}-code-demo")
def tab_button(title, is_active=False):
classes = f"z-10 button-container flex-none relative px-8 py-2 w-[10.59375rem] h-11 rounded-full {center}"
active = f"{classes} current active transition-all duration-300 text-white"
inactive = f"{classes} text-white/80 hover:text-white"
return Li(
Button(
Div(title, cls="m-body w-max mx-auto"),
id=f"tab-{title.lower().replace(' ', '-')}",
cls="toggle-button w-full cursor-pointer",
data_target=f"{title.lower().replace(' ', '-')}-code-demo",
tabindex="-1" if not is_active else None,
name=f"tab-{title.lower().replace(' ', '-')}"),
role="tab", aria_selected="true" if is_active else "false",
cls=active if is_active else inactive)
async def component_preview_section():
cs = await components()
return Section(
Div(
Div(
H2("This home page is a FastHTML app.", cls="text-white heading-2 pt-8"),
P("Click the buttons below to see four small, live components in action.", cls=f"text-white pt-8 l-body {maxrem(32)}"),
cls=f"{maxrem(50)} mx-auto {col} {center} text-center gap-6"
),
*[code_demo(title, file_name, code_snippet, demo_content, i == 0) for i, (title, file_name, code_snippet, demo_content) in enumerate(cs)],
Ul(
*[tab_button(title, i == 0) for i, (title, _, _, _) in enumerate(cs)],
Div(id="highlighter", cls=f"{inset} z-0 highlighter w-[10.59375rem] absolute bg-white/20 h-[2.75rem] rounded-[62.5rem] transition-transform duration-300"),
role="tablist", id="tab-list",
cls=f"relative mt-12 text-white/80 flex-none rounded-[62.5rem] bg-black/20 p-2 max-w-full overflow-x-auto lg:mx-auto {center} hide-scrollbar lg:max-w-[43.375rem]"),
cls=f"{col} {center}"),
cls="relative bg-purple px-4 lg:px-16 pb-24 -mt-8 lg:-mt-10 flex-col xl:items-center items-start gap-6 lg:gap-16 rounded-t-3xl lg:rounded-t-[2.5rem] overflow-x-hidden")
def stacked_cards_section():
return Section(
Div(
Div(
Div(
P("TECH STACK", cls="mono-body text-opacity-60"),
H2("FastHTML scales up and scales down.", cls="text-black heading-2"),
P("Read more about our ",
A("design philosophy here", href="https://fastht.ml/about/vision", **_blank,
cls="border-b-2 border-b-black/30 hover:border-b-black/80"),
", or click a button below:",
cls=f"l-body {maxrem(32)}"),
cls=f"{maxrem(50)} mx-auto {col} {center} text-center gap-6"),
cls="py-8 lg:pt-16 px-4 lg:px-16 rounded-t-3xl lg:rounded-t-[2.5rem] bg-green"),
cls="bg-yellow sticky top-0 bottom-[calc(100%-300px)] w-full"),
Div(
Div(
*[stacked_card(title, description, tech_stack, "bg-soft-green") for title, description, tech_stack in stacked],
id="stacked-cards", cls=f"{maxrem(50)} mx-auto"),
cls="px-4 lg:px-16 w-full bg-green pt-8"),
id="stacked-cards-section", cls="relative")
def samples_section():
text = "FastHTML can be used for everything from collaborative games to multi-modal UI. We've selected small self-contained examples for you to learn from."
return section_wrapper(
(section_header("SAMPLES", "See FastHTML in action", text, max_width=40),
Div(
*[Div(
A(
File(f"assets/{svg}"),
Div(
P(name, cls="border-b-2 border-b-black/30 hover:border-b-black/80 regular-body"),
Img(src=f"{icons}/arrow-up-right.svg", alt="Arrow right icon", cls="group-hover:translate-y-[-0.1rem] transition-all ease-in-out duration-300"),
cls=f"{gap2} transition-transform transform relative items-center mt-4 lg:mt-6"),
href=url, cls=f"{col} items-center"),
cls="group px-2"
) for name, svg, url in samples],
cls="grid max-w-5xl lg:grid-cols-4 lg:max-w-7xl lg:gap-x-12 grid-cols-2 gap-x-4 gap-y-8 w-full mx-auto"),
A("Discover all", href=eg_url,
cls=f"{bnset} bg-black text-white py-2 px-4 s-body rounded-full hover:bg-black/70 transition-colors duration-300")),
bg_color="grey")
def how_it_works_section():
msg = "With FastHTML you create good-looking modern web applications in pure Python and deploy them in minutes."
return section_wrapper(
(Div(
section_header( "GET STARTED IN MINUTES", "The fastest way to create a real web application.", msg),
cls="max-w-3xl w-full mx-auto flex-col items-center text-center gap-6 mb-8 lg:mb-8"),
Div(*[benefit(title, content) for title, content in benefits],
cls=f"{col} w-full lg:flex-row gap-4 items-center lg:gap-8 max-w-7xl mx-auto justify-center"),),
bg_color="yellow", flex=False)
def faq_section():
return section_wrapper(
Div(
section_header( "FAQ", "Questions? Answers.", "Your top FastHTML questions clarified.",
max_width=21, center=False),
Div(
*[faq_item(question, answer, i+3) for i, (question, answer) in enumerate(faqs)],
cls=f"{col} gap-4 {maxrem(32)} transition ease-out delay-[300ms]"),
cls=f"{section_base} w-full mx-auto lg:flex-row items-start max-w-7xl"),
bg_color="blue")
def testimonials_section():
testimonial_cards = [testimonial_card(i, *args) for i, args in enumerate(testimonials)]
return section_wrapper(
Div(
section_header(
"LOVE IS IN THE AIR", "What the experts say",
"Top web programmers tell us that they love working with FastHTML.",
max_width=21, center=False),
carousel(testimonial_cards),
cls=f"{section_base} {maxrem(90)} mx-auto lg:flex-row items-start"),
bg_color="pink")
def footer_link(text, href, **kw):
return Li(A(
Span(text, cls="border-b border-b-transparent border-b-white/50"),
Img(src=f"{icons}/arrow-up-right-white.svg", alt="Arrow right icon", width="16", height="16", cls="w-4 h-4"),
href=href, cls=f"{gap2} items-center hover:text-white border-b border-b-transparent hover:border-b-white", **kw))
def footer():
return Section(
Div(
Div(
P("© 2024 onwards AnswerDotAI. All rights reserved.", cls="mr-auto"),
Nav(
Ul(
footer_link("Github", "https://github.com/AnswerDotAI/fasthtml", **_blank),
footer_link("Join Discord", "https://discord.gg/qcXvcxMhdP", **_blank),
footer_link("Docs", "/docs", **_blank),
footer_link("Site design", "https://tinloof.com/", **_blank),
cls="flex max-lg:flex-col max-lg:items-start gap-4 lg:gap-6 flex-wrap")),
cls="relative z-[1] mono-s flex max-lg:flex-col gap-6 text-white/80 px-4 lg:px-16 pb-16"),
Div(
Div(
File("assets/footer-shapes.svg"),
cls=f"absolute z-0 lg:-top-[15%] top-0 left-1/2 -translate-x-1/2 grid grid-cols-1 grid-rows-1 lg:w-[150%] w-[200%] aspect-square max-w-none min-w-max"),
File("assets/footer-path.svg"),
cls=f"relative z-0 w-full px-4 lg:px-16 pb-1 {col} flex-1 justify-end"),
cls=f"relative w-full h-[420px] lg:h-[600px] {col} pt-8 lg:pt-12 rounded-t-3xl lg:rounded-t-[2.5rem] bg-black overflow-hidden -mt-8 lg:-mt-10"))
hdrs = [
Meta(charset='UTF-8'),
Meta(name='viewport', content='width=device-width, initial-scale=1.0, maximum-scale=1.0'),
Meta(name='description', content=description),
*Favicon('favicon.ico', 'favicon-dark.ico'),
*Socials(title='FastHTML',
description=description,
site_name='fastht.ml',
twitter_site='@answerdotai',
image=f'/assets/og-sq.png',
url=''),
# surrsrc,
Script(src='https://cdn.jsdelivr.net/gh/gnat/surreal@main/surreal.js'),
scopesrc,
Link(href='css/main.css', rel='stylesheet'),
Link(href='css/tailwind.css', rel='stylesheet'),
Link(href='css/stack.css', rel='stylesheet'),
Link(href='css/preview-stack.css', rel='stylesheet'),
Link(href='css/highlighter-theme.css', rel='stylesheet')]
from about.main import app as about_app
routes = [Mount(f"/about", about_app),
Mount('/docs', StaticFiles(directory='docs', html=True)),
]
bodykw = {"class": "relative bg-black font-geist text-black/80 font-details-off"}
app,rt = fast_app(hdrs=hdrs, default_hdrs=False, bodykw=bodykw, on_startup=[startup], routes=routes)
scripts = (
Script(src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js'),
Script(src='https://cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/highlightjs-line-numbers.min.js'),
# Script(src='js/videoPopup.js'),
Script(src='js/pythonHighlighter.js'),
Script(src='js/carouselScroll.js'),
Script(src='js/stack.js'),
Script(src='js/togglePreview.js'),
Script(src='js/codeOverflow.js'),
Script(src='js/copyCode.js'),
)
from fastcore.xtras import timed_cache
@timed_cache(seconds=60)
async def home():
return (Title(f"FastHTML - {description}"),
Main(
hero_section(),
await component_preview_section(),
how_it_works_section(),
stacked_cards_section(),
samples_section(),
faq_section(),
testimonials_section(),
footer()),
*scripts)
@rt("/")
async def get(): return await home()
serve()
|