micheltlutz/winged-swift
**WingedSwift** is an innovative, **open-source** Domain-Specific Language (DSL) library for efficient HTML writing in Swift. Mirroring its Python counterpart, WingedSwift is based on the DSL concept, focusing on simplification and specificity in HTML generation. Using the Compos
π Why WingedSwift?
- π― Type-Safe: Leverage Swift's powerful type system
- π Secure: Built-in XSS protection
- π Fast: Compile-time HTML generation
- π¨ Modern: Full HTML5 support with semantic tags
- π± SEO-Ready: Built-in Open Graph and Twitter Cards
- π οΈ Static Sites: Complete static site generation tools
- π Cross-Platform: Works on macOS, Linux, and Windows
- π€ Open Source: MIT licensed, contributions welcome!
πββοΈ I Need Your Help!
WingedSwift is an open-source project maintained by the community. I actively looking for contributors! Whether you're:
- π A bug hunter
- β¨ A feature enthusiast
- π A documentation lover
- π§ͺ A testing expert
- π A translator
Your contributions are valuable and welcome! Check our Contributing section to get started.
Table of Contents
π Quick Start
For Users
// 1. Add to Package.swift
.package(url: "https://github.com/micheltlutz/Winged-Swift.git", from: "1.3.3")
// 2. Import and use
import WingedSwift
let page = html {
Head(children: [Title(content: "My Site")])
Body(children: [
H1(content: "Hello, WingedSwift!"),
P(content: "Creating HTML with Swift is awesome!")
])
}
print(page.render(pretty: true))For Contributors
# 1. Fork on GitHub, then clone
git clone https://github.com/YOUR_USERNAME/Winged-Swift.git
cd Winged-Swift
# 2. Verify it works
swift build && swift test
# 3. Create a feature branch
git checkout -b feature/my-contribution
# 4. Make changes, test, and submit PR
swift test
# ... make your changes ...
git commit -am "Add: my awesome feature"
git push origin feature/my-contribution
# Then open PR on GitHubSee Contributing for detailed guidelines.
π Getting Started from Scratch
New to WingedSwift? Start here!
Complete Tutorial: See GETTING_STARTED.md for a step-by-step guide.
- Tutorial em PT-BR: Leia o artigo βCriando uma PΓ‘gina EstΓ‘tica com WingedSwift e Tailwind CSSβ
- Tutorial EN-US: Read de post "Building a Static Page with WingedSwift and Tailwind CSS"
Ready-to-use Template: Copy and start coding in minutes!
Tailwind CSS Example: Explore the post-winged-swift repository for a reference project already integrated with Tailwind CSS.
# 1. Copy starter template
cp -r Examples/StarterTemplate ~/MeuSite
cd ~/MeuSite
# 2. Generate your site
swift run
# 3. Open in browser
open dist/index.htmlThe starter template includes:
- β Complete project structure
- β Beautiful responsive design
- β Example pages (Home, About)
- β CSS styles included
- β Build and dev scripts
- β SEO ready (sitemap.xml)
What you get:
MyStaticSite/
βββ Package.swift # SPM configuration
βββ Sources/
β βββ main.swift # Your site generator
β βββ SiteLayout.swift # Reusable layout
βββ Assets/
β βββ css/style.css # Modern CSS
βββ Scripts/
β βββ build.sh # Build script
β βββ dev.sh # Dev server
β βββ deploy.sh # Deploy to GitHub Pages
βββ dist/ # Generated siteLearn more:
- π Complete Getting Started Guide
- π Starter Template
- π‘ Full Examples
Demo
Example Projects
- WingedSwiftDemoVapor - Demo project showing WingedSwift integration
Real-World Sites Built with WingedSwift
Check out these production sites created with WingedSwift:
- NFC Forge β Landing page for the freemium iOS app that writes NFC tags with guided flows, bulk production mode, chip password protection, tag templates, and iCloud sync.
- RideKeeper β Showcase site for the motorcycle maintenance companion app, highlighting multi-motorcycle garage management, fuel tracking, tire monitoring, and upcoming smart reminders.
- ML3dPrint β Catalog site for custom 3D-printed keychains and accessories, featuring NFC-enabled options, personalized orders, and a brand story crafted around technology and motorcycles.
Built with β€οΈ using WingedSwift - showcasing the power of Swift for static site generation!
Installation
Swift Package Manager (Production)
To add WingedSwift to your project, add the following line to your Package.swift file:
dependencies: [
.package(url: "https://github.com/micheltlutz/Winged-Swift.git", from: "1.3.3")
]And include WingedSwift as a dependency in your target:
targets: [
.target(
name: "YourTarget",
dependencies: [
.product(name: "WingedSwift", package: "Winged-Swift")
]
)
]Vapor Integration
To include in Vapor project use this line code in executableTarget:
.product(name: "WingedSwift", package: "Winged-Swift")Local Development (For Contributors)
Want to contribute? Here's how to test your changes locally:
# 1. Fork and clone
git clone https://github.com/YOUR_USERNAME/Winged-Swift.git
cd Winged-Swift
# 2. Test it works
swift build && swift test
# 3. Use in your test project
# In your project's Package.swift:
dependencies: [
.package(path: "../Winged-Swift")
]For complete development workflow, see CONTRIBUTING.md
Usage
WingedSwift allows you to build HTML documents using a DSL syntax in Swift. Here are some examples of how to use the library.
### Basic Example
```swift
import WingedSwift
let document = html {
Head(children: [
Meta(name: "description", content: "A description of the page"),
Link(href: "styles.css", rel: "stylesheet")
])
Body(children: [
Header(children: [
Nav(children: [
A(href: "#home", content: "Home"),
A(href: "#about", content: "About"),
A(href: "#contact", content: "Contact")
])
]),
Main(children: [
P(content: "Welcome to our website!")
]),
Footer(children: [
P(content: "Β© 2024 Company, Inc.")
])
])
}
print(document.render())
```
### Working with Forms
```swift
let form = Form(attributes: [Attribute(key: "action", value: "/submit")], children: [
Fieldset(children: [
Label(for: "name", content: "Name"),
Input(type: "text", name: "name")
]),
Fieldset(children: [
Label(for: "message", content: "Message"),
Textarea(name: "message")
]),
Input(type: "submit", name: "submit", value: "Send")
])
print(form.render())
```
### Code Structure
```swift
let pre = Pre(content: """
This is preformatted text.
It preserves whitespace and line breaks.
""")
print(pre.render())
let code = Code(content: """
let x = 10
print(x)
""")
print(code.render())
let embed = Embed(src: "video.mp4", type: "video/mp4")
print(embed.render())
```
### Pretty Print HTML
WingedSwift now supports pretty printing HTML for better readability during development:
```swift
let page = html {
Head(children: [Title(content: "My Page")])
Body(children: [
H1(content: "Hello World"),
P(content: "This is a paragraph")
])
}
// Compact output (default)
print(page.render())
// Output: <html><head><title>My Page</title></head>...
// Pretty formatted output
print(page.render(pretty: true))
// Output:
// <html>
// <head>
// <title>My Page</title>
// </head>
// <body>
// <h1>Hello World</h1>
// <p>This is a paragraph</p>
// </body>
// </html>
```
### HTML Security (XSS Protection)
By default, all content is automatically escaped to prevent XSS attacks:
```swift
// User input is automatically escaped
let userInput = "<script>alert('XSS')</script>"
let safe = P(content: userInput)
print(safe.render())
// Output: <p><script>alert('XSS')</script></p>
// You can disable escaping when you trust the content
let trusted = P(content: "<b>Bold text</b>", escapeContent: false)
print(trusted.render())
// Output: <p><b>Bold text</b></p>
```
### CSS Helpers
Easily manage CSS classes and IDs with fluent API:
```swift
let card = Div()
.setId("product-card")
.addClass("card")
.addClass("shadow-lg")
.addClasses(["rounded", "p-4"])
.setStyle("background-color: white;")
// Or use arrays directly
let container = Div()
.addClasses(["container", "mx-auto", "flex"])
```
### Data Attributes & ARIA Support
Add data attributes and ARIA labels for better accessibility:
```swift
let button = Button()
.dataAttribute(key: "toggle", value: "modal")
.dataAttribute(key: "target", value: "#myModal")
.ariaAttribute(key: "label", value: "Close modal")
.setRole("button")
let nav = Nav()
.dataAttributes([
"component": "navbar",
"version": "2.0"
])
.ariaAttributes([
"label": "Main navigation",
"expanded": "true"
])
```
### HTML5 Semantic Tags
Full support for modern HTML5 semantic elements:
```swift
let blog = Article(children: [
H1(content: "Article Title"),
Time(datetime: "2024-01-15", content: "January 15, 2024"),
P(content: "Article content..."),
Aside(children: [
H3(content: "Related"),
P(content: "Related content")
])
])
let imageWithCaption = Figure(children: [
Img(src: "photo.jpg", alt: "Beautiful sunset"),
Figcaption(content: "A beautiful sunset over the ocean")
])
let highlighted = P(children: [
HTMLTag("span", content: "This is "),
Mark(content: "highlighted"),
HTMLTag("span", content: " text")
])
```
### SEO Helpers
Built-in helpers for Open Graph, Twitter Cards, and common SEO meta tags:
```swift
import WingedSwift
// Complete SEO setup
let seoTags = SEO.complete(
title: "My Awesome Website",
description: "The best website ever created",
image: "https://example.com/og-image.jpg",
url: "https://example.com",
keywords: ["swift", "html", "static-site"],
author: "Your Name",
twitterSite: "@yoursite",
twitterCreator: "@yourcreator"
)
let page = html {
Head(children: [
Title(content: "My Awesome Website")
] + seoTags)
Body(children: [
H1(content: "Welcome!")
])
}
// Or use individual helpers
let ogTags = SEO.openGraph(
title: "Page Title",
description: "Page Description",
image: "https://example.com/image.jpg",
url: "https://example.com/page"
)
let twitterTags = SEO.twitterCard(
title: "Page Title",
description: "Page Description",
image: "https://example.com/image.jpg",
site: "@site",
creator: "@creator"
)
```
### Static Site Generator
Generate complete static websites with ease:
```swift
import WingedSwift
let generator = StaticSiteGenerator(outputDirectory: "./dist")
// Create your pages
let homePage = html {
Head(children: [
Meta(charset: "UTF-8"),
Title(content: "Home"),
Link(href: "css/style.css", rel: "stylesheet")
])
Body(children: [
H1(content: "Welcome to my site!"),
P(content: "This is a static site generated with WingedSwift")
])
}
let aboutPage = html {
Head(children: [
Meta(charset: "UTF-8"),
Title(content: "About"),
Link(href: "css/style.css", rel: "stylesheet")
])
Body(children: [
H1(content: "About Us"),
P(content: "Learn more about our project")
])
}
// Generate files
try generator.clean() // Clean output directory
try generator.generate(page: homePage, to: "index.html", pretty: true)
try generator.generate(page: aboutPage, to: "about.html", pretty: true)
// Copy assets
try generator.copyAsset(from: "./assets/css", to: "css")
try generator.copyAsset(from: "./assets/images", to: "images")
// Generate sitemap
let sitemapUrls = [
SitemapURL(loc: "https://example.com/", changefreq: "daily", priority: 1.0),
SitemapURL(loc: "https://example.com/about", changefreq: "monthly", priority: 0.8)
]
let sitemap = SitemapGenerator.generate(urls: sitemapUrls)
try generator.writeFile(content: sitemap, to: "sitemap.xml")
// Generate RSS feed
let rssGen = RSSGenerator(
title: "My Blog",
link: "https://example.com",
description: "A blog about Swift and web development"
)
let rssItems = [
RSSItem(
title: "First Post",
link: "https://example.com/posts/first",
description: "My first blog post",
pubDate: "Mon, 15 Jan 2024 12:00:00 GMT"
)
]
let rssFeed = rssGen.generate(items: rssItems)
try generator.writeFile(content: rssFeed, to: "feed.xml")
```
### Layout System
Create reusable layouts with the Layout protocol:
```swift
import WingedSwift
class BlogLayout: Layout {
let siteTitle: String
let stylesheets: [String]
init(siteTitle: String, stylesheets: [String] = []) {
self.siteTitle = siteTitle
self.stylesheets = stylesheets
}
func render(content: HTMLTag) -> HTMLTag {
return html {
Head(children: [
Meta(charset: "UTF-8"),
Meta(name: "viewport", content: "width=device-width, initial-scale=1.0"),
Title(content: siteTitle)
] + stylesheets.map { Link(href: $0, rel: "stylesheet") })
Body(children: [
Header(children: [
H1(content: siteTitle),
Nav(children: [
A(href: "/", content: "Home"),
A(href: "/about", content: "About"),
A(href: "/blog", content: "Blog")
])
]),
Main(children: [content]),
Footer(children: [
P(content: "Β© 2024 My Blog. All rights reserved.")
])
])
}
}
}
// Use the layout
let layout = BlogLayout(
siteTitle: "My Blog",
stylesheets: ["css/main.css", "css/blog.css"]
)
let postContent = Article(children: [
H2(content: "My First Post"),
P(content: "This is the content of my first post...")
])
let page = layout.render(content: postContent)
try generator.generate(page: page, to: "blog/first-post.html", pretty: true)
```Features
- β Type-Safe DSL: Leverage Swift's type system for HTML generation
- β Pretty Print: Format HTML output for development and debugging
- β XSS Protection: Automatic content escaping to prevent attacks
- β HTML5 Support: Complete set of modern semantic tags
- β CSS Helpers: Fluent API for classes, IDs, and inline styles
- β Accessibility: Built-in ARIA and data attribute helpers
- β SEO Optimized: Open Graph, Twitter Cards, and meta tag generators
- β Static Site Generation: Build complete static websites
- β Layout System: Reusable templates with the Layout protocol
- β Sitemap & RSS: Automatic sitemap.xml and RSS feed generation
- β Asset Management: Easy copying and organizing of assets
- β Cross-Platform: Works on macOS, Linux, and Windows
- β Well Tested: Comprehensive test coverage (CI runs on macOS & Linux)
- β Zero Dependencies: Pure Swift implementation (Foundation only)
π Platform Support
WingedSwift is cross-platform and works on:
- β macOS (10.15+)
- β Linux (Ubuntu, Debian, Fedora, etc.)
- β Windows (with Swift for Windows)
Why Cross-Platform?
- Uses only Foundation APIs - no platform-specific code
- Perfect for server-side Swift deployments
- CI/CD friendly - runs on any platform
- Deploy static sites from any environment
Tested On
Our GitHub Actions CI tests on:
- Ubuntu Latest (Linux)
- macOS Latest
See our test workflow for details.
Linux Example
# On Ubuntu/Debian
apt-get update
apt-get install -y swift
# Create your site
swift package init --type executable
# Edit Package.swift and main.swift
swift run
# Deploy from Linux server! πNote: When creating projects, do not specify platforms in your Package.swift to maintain cross-platform compatibility.
// β Don't do this (restricts to macOS only)
platforms: [.macOS(.v13)]
// β
Do this (works everywhere)
// Just omit the platforms fieldDocumentation
The complete documentation is available at:
π https://micheltlutz.github.io/Winged-Swift/
π Setup Guide: See GITHUB_PAGES_SETUP.md for detailed instructions on configuring GitHub Pages.
Generating the Documentation
To generate the DocC documentation, use the following command in the terminal:
swift package --allow-writing-to-directory ./docs generate-documentation --target WingedSwift --output-path ./docs --transform-for-static-hosting --hosting-base-path /Winged-SwiftImportant: The generated documentation uses absolute paths and must be served via HTTP, not opened directly as a file.
Viewing Documentation Locally
Option 1: Use Preview Mode (Recommended - Easiest)
swift package --disable-sandbox preview-documentation --target WingedSwiftThen access: http://localhost:8080/documentation/wingedswift
This is the simplest way to preview documentation locally. No setup needed!
Option 2: Serve Generated Docs with HTTP Server
The generated documentation uses absolute paths designed for GitHub Pages. To view it locally, you need to serve it with a proper HTTP server structure.
Using the provided script:
./Scripts/view-docs.shThis script creates a temporary directory structure and serves the docs correctly.
Note: The generated documentation in docs/ is optimized for GitHub Pages deployment. For local development, the preview mode (Option 1) is recommended as it's simpler and doesn't require manual server setup.
Important: Do not open docs/index.html directly in a browser (file://). It will show a blank page because it uses absolute paths designed for GitHub Pages hosting.
π€ Contributing
We love contributions! WingedSwift is an open-source project and your help makes it better.
Quick Start for Contributors
# 1. Fork & Clone
git clone https://github.com/YOUR_USERNAME/Winged-Swift.git
cd Winged-Swift
# 2. Verify it works
swift build && swift test
# 3. Create a feature branch
git checkout -b feature/my-awesome-feature
# 4. Make changes, test, and submit PRWays to Contribute
- π Report Bugs - Open an issue
- β¨ Request Features - Share your ideas
- π§ Submit Code - Fix bugs or add features
- π Improve Docs - Help others understand
- π¬ Help Others - Answer questions
- π Translate - Make it accessible worldwide
π Read the Complete Guide
For detailed instructions on:
- Setting up your development environment
- Code style guidelines
- Testing requirements
- Commit message format
- Pull request process
- And much more...
See our complete Contributing Guide
Recognition
All contributors are recognized in our release notes. Thank you for making WingedSwift better! π
Code of Conduct
Please note that this project is released with a Code of Conduct. By participating, you agree to abide by its terms.
π Project Status
WingedSwift is actively maintained and welcoming contributions!
- β Current Version: 1.3.3
- π Status: Active Development
- π Test Coverage: High
- π Release Cycle: Regular updates
- π¬ Community: Growing
What's Next?
Check our Issues for:
- π Good First Issues: Perfect for newcomers
- β¨ Feature Requests: Ideas from the community
- π Help Wanted: Issues where we need your expertise
π¬ Community & Support
Get in Touch
- π Discussions: GitHub Discussions
- π Issues: Report bugs or request features
- π§ Email: Contact via michel@micheltlutz.me
- π§ Site:micheltlutz.me
- π§ Linkedin:My Linkedin
Stay Updated
- β Star the repository to show support
- ποΈ Watch for updates and releases
- π Follow for announcements
Show Your Support
If WingedSwift is helpful for your project:
- β Star the repository
- π¦ Share on social media
- π Write a blog post or tutorial
- π¬ Recommend to friends and colleagues
- π€ Contribute code, docs, or ideas
π Changelog
See CHANGELOG.md for a detailed history of changes.
License
This project is licensed under the MIT License. See the LICENSE file for more details.
Package Metadata
Repository: micheltlutz/winged-swift
Default branch: main
README: README.md