{"id":13535180,"url":"https://github.com/Alja7dali/swift-web-page","last_synced_at":"2025-04-02T00:32:42.340Z","repository":{"id":131322672,"uuid":"398653475","full_name":"Alja7dali/swift-web-page","owner":"Alja7dali","description":"📄 A Swift DSL for writing type-safe HTML/CSS in SwiftUI way","archived":false,"fork":false,"pushed_at":"2022-06-18T12:36:18.000Z","size":206,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-29T05:32:56.236Z","etag":null,"topics":["css","declarative-programming","declarative-ui","dsl","generate-css","generate-html","html","rendering","resultbuilder","server-side-swift","swep","swift","swift-web-page","swiftui","webpage","website"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Alja7dali.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-08-21T20:48:17.000Z","updated_at":"2024-06-16T09:54:40.000Z","dependencies_parsed_at":"2023-07-31T09:00:27.563Z","dependency_job_id":null,"html_url":"https://github.com/Alja7dali/swift-web-page","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alja7dali%2Fswift-web-page","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alja7dali%2Fswift-web-page/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alja7dali%2Fswift-web-page/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Alja7dali%2Fswift-web-page/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Alja7dali","download_url":"https://codeload.github.com/Alja7dali/swift-web-page/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246735354,"owners_count":20825221,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["css","declarative-programming","declarative-ui","dsl","generate-css","generate-html","html","rendering","resultbuilder","server-side-swift","swep","swift","swift-web-page","swiftui","webpage","website"],"created_at":"2024-08-01T08:00:50.848Z","updated_at":"2025-04-02T00:32:41.901Z","avatar_url":"https://github.com/Alja7dali.png","language":"Swift","funding_links":[],"categories":["HTML"],"sub_categories":[],"readme":"\n# 📄 swift-web-page (swep)\n\n\u003cp align=\"left\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Swift_Version-5.1-orange.svg?style=flat\u0026logo=Swift\" alt=\"Swift Version: 5.1\"/\u003e\n  \u003ca href=\"https://swift.org/package-manager\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/SwiftPM-Compatible-darkgreen.svg?style=flat\" alt=\"Swift Package Manager\"/\u003e\n  \u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Platforms-macOS,%20iOS,%20Linux-darkgreen.svg?style=flat\" alt=\"Swift Package Manager\"/\u003e\n\u003c/p\u003e\n\nSwep is a Swift DSL for writing type-safe HTML/CSS in SwiftUI way.\n\nCheckout [SwepUI](https://github.com/Alja7dali/swift-web-page-ui) for SwiftUI-like encapsulation!\n\n## Table of Contents\n\n  - [Motivation](#motivation)\n  - [Examples](#examples)\n  - [Safety](#safety)\n  - [Design](#design)\n  - [FAQ](#faq)\n  - [Installation](#installation)\n  - [Real world example](#real-world-example)\n  - [License](#license)\n\n## Motivation\n\nThe popular choice for rendering HTML/CSS in Swift these days is to use templating languages, but they expose your application to **runtime errors** and **invalid HTML/CSS**. Our library prevents these runtime issues at compile-time by embedding HTML/CSS directly into Swift’s powerful type system.\n\n## Examples\n\nHTML/CSS documents can be created in a SwiftUI-like fashion, much like you might create a nested SwiftUI View:\n\n```swift\nimport Swep\n\nlet page = document {\n  html {\n    head(title(\"YOLO!\"))\n    body {\n      h1(\"Welcome!\")\n      p(\"You've found our site!\")\n    }\n  }\n}\n```\n\nCSS inside Style tag!\n\n```swift\nimport Swep\n\nlet page = document {\n  html {\n    head {\n      title(\"YOLO!\")\n      style {\n        selector(\"h1\") {\n          color(.tomato())\n        }\n      }\n    }\n    body {\n      h1(\"Welcome!\")\n      p(\"You've found our site!\")\n    }\n  }\n}\n```\n\nEven better, CSS as tag modifier\n\n```swift\nimport Swep\n\nlet page = document {\n  html {\n    head(title(\"YOLO!\"))\n    body {\n      h1(\"Welcome!\")\n        .color(.tomato())\n      p(\"You've found our site!\")\n    }\n  }\n}\n```\n\nOnce your document is created you can render it using the `render` function:\n\n```swift\n/// when you just want to see the output..\npage.render(.debug(.pretty(.spaces(2))))\n// or\npage.debugRender()\n```\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003e\n      YOLO!\n    \u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1 style=\"color:rgba(255,99,71,1.0)\"\u003e\n      Welcome!\n    \u003c/h1\u003e\n    \u003cp\u003e\n      You've found our site!\n    \u003c/p\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n```swift\n/// when your document is ready for release..\npage.render(.release(.inline))\n// or\npage.render()\n```\n\n```html\n\u003c!DOCTYPE html\u003e\u003chtml\u003e\u003chead\u003e\u003ctitle\u003eYOLO!\u003c/title\u003e\u003c/head\u003e\u003cbody\u003e\u003ch1 style=\"color:rgba(255,99,71,1.0);\"\u003eWelcome!\u003c/h1\u003e\u003cp\u003eYou\u0026apos;ve found our site!\u003c/p\u003e\u003c/body\u003e\u003c/html\u003e\n```\n\n## Safety\n\nBecause we are embedding our DSL in Swift we can take advantage of some advanced Swift features to add an extra layer of safety when constructing HTML/CSS documents. For a simple example, we can strengthen many HTML/CSS APIs to force their true types rather than just relying on strings. \n\n```swift \nlet imgTag = img()\n               .src(\"cat.jpg\")\n               .width(400)\n               .height(300)\n\nimgTag.render()\n// \u003cimg src=\"cat.jpg\" width=\"400\" height=\"300\"\u003e\n```\n\nHere the `src` attribute takes a string, but `width` and `height` take integers, as it’s invalid to put anything else in those attributes.\n\nFor a more advanced example, `\u003cli\u003e` tags can only be placed inside `\u003col\u003e` and `\u003cul\u003e` tags, and we can represent this fact so that it’s impossible to construct an invalid document:\n\n```swift\nlet listTag = ul {\n  li(\"Cat\")\n  li(\"Dog\")\n  li(\"Rabbit\")\n} // ✅ Compiles!\n\nlistTag.render()\n// \u003cul\u003e\u003cli\u003eCat\u003c/li\u003e\u003cli\u003eDog\u003c/li\u003e\u003cli\u003eRabbit\u003c/li\u003e\u003c/ul\u003e\n\ndiv {\n  li(\"Cat\")\n  li(\"Dog\")\n  li(\"Rabbit\")\n} // 🛑 Compile error\n```\n\nAnother advance example, `\u003cthead\u003e`, `\u003ctbody\u003e`, and `\u003ctfoot\u003e` tags can only be placed inside `\u003ctable\u003e` tags, same for `\u003cth\u003e`, and `\u003ctd\u003e` tags can only be placed inside `\u003ctr\u003e` tags.\n\n```swift\nlet tableTag = table {\n  tr(th(\"A Head\"))\n  tr(td(\"A Body\"))\n} // ✅ Compiles!\n\ntype(of: tableTag)\n// Table\u003cTuple\u003c(Tr\u003cTh\u003cText\u003e\u003e, Tr\u003cTd\u003cText\u003e\u003e)\u003e\u003e\n\nlet tableTag = table {\n  thead(tr(th(\"A Head\")))\n  tbody(tr(td(\"A Body\")))\n} // ✅ Compiles!\n\ntype(of: tableTag)\n// Table\u003cTuple\u003c(Thead\u003cTr\u003cTh\u003cText\u003e\u003e\u003e, Tbody\u003cTr\u003cTd\u003cText\u003e\u003e\u003e)\u003e\u003e\n\nlet tableTag = table {\n  thead(tr(th(\"A Head\")))\n  for _ in 1...3 {\n    tbody(tr(td(\"A Body\")))\n  }\n} // ✅ Compiles!\n\ntype(of: tableTag)\n// Table\u003cTuple\u003c(Thead\u003cTr\u003cTh\u003cText\u003e\u003e\u003e, Array\u003cTbody\u003cTr\u003cTd\u003cText\u003e\u003e\u003e\u003e)\u003e\u003e\n\ntable {\n  tbody(tr(td(\"A Body\")))\n  thead(tr(th(\"A Head\")))\n} // 🛑 Compile error\n```\n\nThere alot more..\n\n## Design\n\nBehind the scenes **Swep** is following the **Protocol-Oriented-Programming** (**POP**) approach, along with the powerful swift-feature **[@resultBuilder](https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md)**. There are two main-libraries *Html* handles the html-side, and *Css* handles the css-side. And one micro-library *HtmlCssSupport* which handles the combination of both *Html*, and *Css*.\n\n## FAQ\n\n### Can I use this with existing Swift web frameworks like Vapor, Kitura, and Perfect?\n\nYes! We even provide plug-in libraries that reduce the friction of using this library with Vapor, Kitura, and Perfect. Find out more information at the following repos:\n\n- [swift-web-page-vapor](https://github.com/alja7dali/swift-web-page-vapor)\n- [swift-web-page-kitura](https://github.com/alja7dali/swift-web-page-kitura)\n- [swift-web-page-perfect](https://github.com/alja7dali/swift-web-page-perfect)\n \n### Why would I use this over a templating language?\n\nTemplating languages are popular and easy to get started with, but they have many drawbacks:\n\n1. **Stringy APIs**: Templating languages are always stringly typed because you provide your template as a big ole string, and then at runtime the values are interpolated and logic is executed. This means things we take for granted in Swift, like the compiler catching typos and type mismatches, will go unnoticed until you run the code.\n\n1. **Incomplete language**: Templating languages are just that: programming languages. That means you should expect from these languages all of the niceties you get from other fully-fledged languages like Swift. That includes syntax highlighting, IDE autocompletion, static analysis, refactoring tools, breakpoints, debugger, and a whole slew of features that make Swift powerful like let-bindings, conditionals, loops and more. However, the reality is that no templating language supports all of these features.\n\n1. **Rigid**: Templating languages are rigid in that they do not allow the types of compositions and transformations we are used to performing on data structures in Swift. It is not possible to succinctly traverse over the documents you build, and inspect or transform the nodes you visit. This capability has many applications, such as being able to pretty print or minify your HTML/CSS output, or writing a transformation that allows you to inline a CSS stylesheet into an HTML/CSS node. There are entire worlds closed off to you due to how templating languages work.\n\nThe DSL in this library fixes all of these problems, and opens up doors that are completely closed to templating languages.\n\n### When is it more appropriate to use a templating language over swift-web-page?\n\nThere are a few reasons you might want to still use a templating language:\n\n1. A designer delivers a large HTML/CSS document to you and all you want to do is hook in a little bit of value interpolation or logic. In this case you can simply copy and paste that HTML/CSS into your template, add a few interpolation tokens, and you're well on your way to having a full page served from your web application.\n\n1. You need to render non-HTML/CSS documents. The beauty of templating languages is that it outputs straight to plain text, and so it can model any type of document, whether it be HTML/CSS, markdown, XML, RSS, ATOM, LaTeX, and more.\n\n1. Creating _very_ large documents in a single expression can cause compile times to go up, whereas templates are not compiled by Swift and so do not influence compile times. Luckily this isn't a problem too often because it is very easy to break up a document into as many small pieces as you want, which will probably lead to more reusable code in the long run.\n\nIf you do decide that a templating language better suites your needs, then you should consider [HypertextLiteral](https://github.com/NSHipster/HypertextLiteral), which gives you template-like capabilities but in a safer manner.\n\n## Real world example\n\nCreating a html document, and css stylesheet\n\n```swift\nlet titillimFont: StaticString = \"\"\"\n  https://fonts.googleapis.com/css2?family=\\\n  Titillium+Web:ital,wght@0,200;0,300;0,400;\\\n  0,600;0,700;0,900;1,200;1,300;1,400;1,600;\\\n  1,700\u0026display=swap\n  \"\"\"\n  \nlet plainStyle = style {\n  `import`(titillimFont)\n  selector(\"*, *::before, *::after\") {\n    margin(.px(0))\n    padding(.px(0))\n    boxSizing(.borderBox)\n  }\n  selector(\"body\") {\n    margin(.px(0), .auto)\n    backgroundColor(.hex(0x111))\n    fontFamily(\"'Titillium Web', sans-serif\")\n  }\n}\n\nlet page = document {\n  html {\n    head {\n      title(\"Hello, Swep!\")\n      plainStyle\n    }\n    body {\n      h1(\"📄 swift-web-page (swep)\")\n      hr()\n        .width(50%)\n        .minWidth(.px(720))\n        .color(.hex(0x323232))\n      p {\n        strong(\"Swep \")\n        text(\"is a Swift DSL for writing type-safe HTML/CSS in declarative way.\")\n      }\n      p {\n        text(\"With \")\n        strong(\"Swep:\")\n      }\n      ul {\n        li(\"You can write html documents along with css\")\n          .fontWeight(.bolder)\n        li(\"Bringing all swift-language features out of the box\")\n        #if swift(\u003e=5.1)\n          for version in 1...4 {\n            if version != 2 {\n              li(\"supporting swift v5.\\(version)\")\n            }\n          }\n        #else\n          li(\"Unfortunately this library is built using @resultBuilder which is available in swift v5.1 and higher 😢\")\n        #endif\n      }\n      blockquote(\"Enjoy! ✌️😁\")\n    }\n  }\n}\n```\n\nChecking out the type of the document..\n```swift\ntype(of: page.content)\n```\n\n```bash\nHtml\u003cTuple\u003c(Head\u003cTuple\u003c(Title\u003cText\u003e, Style\u003cStylesheet\u003e)\u003e\u003e, Body\u003cTuple\u003c(H1\u003cText\u003e, Hr, P\u003cTuple\u003c(Strong\u003cText\u003e, Text)\u003e\u003e, P\u003cTuple\u003c(Text, Strong\u003cText\u003e)\u003e\u003e, Ul\u003cTuple\u003c(Li\u003cText\u003e, Li\u003cText\u003e, Array\u003cOptional\u003cLi\u003cText\u003e\u003e\u003e)\u003e\u003e, Blockquote\u003cText\u003e)\u003e\u003e)\u003e\u003e\n```\n\nRendering out the document..\n\n```swift\nlet renderMode: RenderMode = .release(.pretty(.spaces(2)))\npage.render(renderMode)\n```\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003e\n      Hello, Swep!\n    \u003c/title\u003e\n    \u003cstyle\u003e\n      @import https://fonts.googleapis.com/css2?family=Titillium+Web:ital,wght@0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700\u0026display=swap;\n      *, *::before, *::after {\n        margin: 0px;\n        padding: 0px;\n        -webkit-box-sizing: border-box;\n        -moz-box-sizing: border-box;\n        box-sizing: border-box;\n      }\n      body {\n        margin: 0px auto;\n        background-color: #111;\n        font-family: 'Titillium Web', sans-serif;\n      }\n    \u003c/style\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003e\n      📄 swift-web-page (swep)\n    \u003c/h1\u003e\n    \u003chr style=\"width:50%;\n               min-width:720px;\n               color:#323232\"\u003e\n    \u003cp\u003e\n      \u003cstrong\u003e\n        Swep \n      \u003c/strong\u003e\n      is a Swift DSL for writing type-safe HTML/CSS in declarative way.\n    \u003c/p\u003e\n    \u003cp\u003e\n      With \n      \u003cstrong\u003e\n        Swep:\n      \u003c/strong\u003e\n    \u003c/p\u003e\n    \u003cul\u003e\n      \u003cli style=\"font-weight:bolder\"\u003e\n        You can write html documents along with css\n      \u003c/li\u003e\n      \u003cli\u003e\n        Bringing all swift-language features out of the box\n      \u003c/li\u003e\n      \u003cli\u003e\n        supporting swift v5.1\n      \u003c/li\u003e\n      \u003cli\u003e\n        supporting swift v5.3\n      \u003c/li\u003e\n      \u003cli\u003e\n        supporting swift v5.4\n      \u003c/li\u003e\n    \u003c/ul\u003e\n    \u003cblockquote\u003e\n      Enjoy! ✌️😁\n    \u003c/blockquote\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n## Installation\n\n### Swift Package Manager (SPM)\n\nIf you want to use *swift-web-page* in a project that uses [SPM](https://swift.org/package-manager/), it's as simple as adding a `dependencies` clause to your `Package.swift`:\n\n``` swift\ndependencies: [\n  .package(url: \"https://github.com/alja7dali/swift-web-page.git\", from: \"0.0.1\")\n]\n```\n\nFrom there you can add `Swep` as target dependencies.\n\n``` swift\nlet Swep: Target.Dependency = .product(name: \"Swep\", package: \"swift-web-page\")\n...\ntargets: [\n  .target(name: \"yourProject\", dependencies: [Swep]),\n]\n```\n\n## License\n\nAll modules are released under the MIT license. See [LICENSE](./LICENSE.md) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAlja7dali%2Fswift-web-page","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAlja7dali%2Fswift-web-page","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAlja7dali%2Fswift-web-page/lists"}