Releases: varabyte/kobweb
v0.20.0
This release is nearly identical to 0.19.3 (with minor changes discussed below) but enables support for Kotlin 2.1.0.
kobweb = "0.20.0"
kotlin = "2.1.0"
Important
Planning to upgrade? Review instructions in the README.
Changes
- Refactored code behind the scenes resulting in a Compose optimization that reduces site output size by a modest 2-3%
Canvas
widgets now detect if their lambda block changes upon subsequent recompositions.- See notes.
Notes
Canvas lambdas
When I first wrote the canvas widget, I basically expected the render callback passed into it itself to never change over time.
For example, refer to this clock rendering demo. Obviously, each time the render call happens, you'll get a new result (as it is based on the current time), but the callback logic itself is unchanging and self contained.
However, if you wrote a lambda that, for example, captured some values, then that lambda will be recreated each time the method is recomposed. Here, this lambda will be regenerated for each time a new cursor position is passed into the parent function:
fun CanvasWithCursor(cursorX: Int, cursorY: Int) {
val cursor: CanvasImageSource = remember { initializeCursor() }
Canvas2d(/*... params ...*/) {
// canvas lambda captures cursorX and cursorY
ctx.drawImage(cursor, cursorX.toDouble(), cursorY.toDouble())
}
}
Previously, the Canvas2d
internally remembered the first lambda that was passed in and ignored all subsequent changes.
This is not an issue that perhaps 99.9% of Kobweb users would ever run into -- especially given that this bug has been present for almost three years at this point and was just recently reported -- but I didn't know how else to explain this feature without describing it in this much detail!
Thanks
Thanks to @DennisTsar for doing the early legwork on investigating 2.1.0, including a way to get past a roadblock that would have otherwise significantly delayed the release, as well as for identifying the refactoring that would cut our site size down.
Full Changelog: v0.19.3...v0.20.0
v0.19.3
Some fun miscellaneous items in this one, including breakpoint ranges, Kotlinx serialization support for HTTP verbs, and support for small/large/dynamic viewport units.
Important
Planning to upgrade? Review instructions in the README.
Changes
Frontend
- Fixed development live reload behavior, which now works even if the tab is not visible when the site is finished rebuilding.
- HTTP fetch methods now support serialization.
- See "HTTP verb serialization" notes section for more details.
- You can now specify breakpoint ranges in CSS styles.
- For example:
(Breakpoint.SM until Breakpoint.LG) { ... }
targets tablet and desktop screens (but no wider). - The README breakpoints section has been updated with more examples.
- For example:
- New
Modifier.attr
andModifier.dataAttr
attribute modifiers.Modifier.attr("key", "value")
is a slightly simpler substittute forModifier.attrsModifier { attr("key", "value") }
Modifier.dataAttr("key", "value")
is short forModifier.attrsModifier { attr("data-key", "value") }
- Added support for repainting canvases manually.
- See "Repainting canvases manually" notes section for more details.
- New CSS units for small / large / dynamic viewport sizes
- These modern (and relatively recent) units let designers create sites that respond better to mobile devices that have UI elements that hide / show regularly.
- See this article for examples.
- Fixed a reported issue where attempting to query
AppGlobals
inInitSilk
andInitKobweb
methods would crash. ⚠️ routePrefix
has been renamed tobasePath
- You will be warned to update this value in your
conf.yaml
file. - This name is more consistent with what other web frameworks use.
- You will be warned to update this value in your
- Simplified a bunch of APIs that used to take
autoPrefix
parameters.- It is not expected that any user outside of the Kobweb library internally actually used this parameter, but they have been removed. At this point, if you set a
basePath
value in yourconf.yaml
file, you should just expect that Kobweb will automatically add it every time.
- It is not expected that any user outside of the Kobweb library internally actually used this parameter, but they have been removed. At this point, if you set a
Backend
- Updated ktor to v3.0.0
- And migrated / cleaned up code related to SSE (server-sent events), which is used for live reloading.
- Fixed an issue with spaces in paths in our
start.bat
server script. - Server start scripts now include the server layout
- In other words,
kobweb export --layout fullstack
andkobweb export --layout static
generate slightly different scripts.
- In other words,
Build
- Jetbrains Compose dependency updated to 1.7.1
- Disable compose trace markers for kobweb modules.
- This was extra compose code that had no effect for Compose HTML sites, so disabling it should result in some JS file size savings.
Notes
HTTP verb + serialization
The HTTP standard provides a core set of verbs (GET, POST, PUT, etc.) which Kobweb provides Kotlin bindings for. For example:
// `get` returns a ByteArray, which we convert into a string. It is up to us to interpret that further,
// perhaps by parsing it
val unparsedData = window.api.get("/user/123").decodeToString()
With this latest release, if your site depends on kotlinx serialization (setup instructions here) and a kobweb serialization artifact (discussed shortly), you can leverage it to get a fully parsed type.
First, you should add a dependency on the artifact that provides the utility methods:
# libs.versions.toml
[libraries]
kobwebx-serialization-kotlinx = { module = "com.varabyte.kobwebx:kobwebx-serialization-kotlinx", version.ref = "kobweb" }
// site/build.gradle.ktx
// apply kotlinx serialization plugin
jsMain.dependencies {
// ...
// add kotlinx serialization dependency
implementation(libs.kobwebx.serialization.kotlinx)
}
And that point, your project should have access to kotlinx-serialization-aware methods:
@Serializable
class UserDataResponse(val id: String, val name: String, val age: Int)
val userData = window.api.get<UserDataResponse>("/user/123")
check(userData.id == "123")
You can also pass in rich types for the body parameter, if you have one:
@Serializable
class ChangeUserNameRequest(val id: String, val name: String)
@Serializable
class ChangeUserNameResponse(val error: String?)
val response = window.api.put<ChangeUserNameRequest, ChangeUserNameResponse>(
"/user/change-name", body = ChangeUserNameRequest("123", "Kobweb")
)
if (response.error != null) { /* ... */ }
Repainting canvases manually
Canvases (specifically, Canvas2d
and CanvasGl
widgets) were designed to repaint continuously, which was honestly inspired by my background in gamedev. However, it is absolutely valid to have a canvas that only paints once and then stops until the user interacts with the canvas, or at least, whose repaint is infrequent and controlled by the developer.
To this end, we introduced the new CanvasRepainter
class, which you can optionally pass into a canvas and use it to control when that canvas rerenders:
// Before: Continuously rendering canvas
Canvas2d(width, height, minDeltaMs = ONE_FRAME_MS_60_FPS) {
/* rendered about every 15ms */
}
// New: Repaint on click
val repainter = remember { CanvasRepainter() }
Canvas2d(width, height, Modifier.onClick { repainter.repaint() }, repainter = repainter) {
/* rerendered every time the user clicks on this canvas */
}
Here is an example which refills the canvas with a random color every click:
const val WIDTH = 320
const val HEIGHT = 240
// Later...
val repainter = remember { CanvasRepainter() }
Canvas2d(
WIDTH, HEIGHT,
Modifier.onClick { repainter.repaint() },
repainter = repainter
) {
ctx.fillStyle = Color.rgb(Random.nextInt(255), Random.nextInt(255), Random.nextInt(255))
ctx.fillRect(0.0, 0.0, WIDTH.toDouble(), HEIGHT.toDouble())
}
If you don't care about repainting at all and just want a canvas that renders something unique once and then never again, you can pass in the REPAINT_CANVAS_MANUALLY
value to the minDeltaMs
parameter and then just never request a repaint:
Canvas2d(width, height, minDeltaMs = REPAINT_CANVAS_MANUALLY) {
/* renders exactly once */
}
A note on tryRoutingTo
/ navigateTo
and base paths
If you have RoutePrefix.prepend
code anywhere in your project (which is probably very few of you all):
ctx.router.tryRoutingTo(RoutePrefix.prepend("/"))
it is no longer necessary to auto-prepend anything yourself. In this release, tryRoutingTo
and navigateTo
have been updated to prepend these values for you automatically.
In other words, you can find those calls and simplify your code to:
ctx.router.tryRoutingTo("/")
and it will just work.
Old code will continue to work for now, but if you look at your dev console output, you'll find warnings asking the developer to search your code and fix this issue.
If you have code where you still need to keep the RoutePrefix.prepend
call in (because, say, you're interacting with a Compose HTML widget that is not aware of Kobweb base paths), the name has been deprecated and you should change it to BasePath.prepend
instead.
Thanks!
-
Thanks to new contributor @ChrisGNZ for reporting and helping us fix the issue with our
start.bat
script (which broke if the user's Java path had at least one space in it...) -
Thanks to @dead8309 for their spooky Hacktoberfest contribution, fixing #602 and giving us serialization APIs for HTTP verbs.
-
Thanks to @TheDome0 for their help extending CSS units.
-
And to @DennisTsar for so much of his work, as usual, but with a special callout for the ktor 3.0.0 migration, removing unnecessary trace markers (resulting in smaller sites for everyone), and some work he has done in preparation for Kotlin 2.1.0 (to be supported in a future release)
Full Changelog: v0.19.2...v0.19.3
v0.19.2
This release is nearly identical to v0.19.1 but contains a fix for a logic error introduced there that affected scrolling to fragments on the same page.
For example, if your project had code like this:
Link("#conclusion", "Click here to jump to the conclusion")
clicking on that link would have no effect.
If you are using v0.19.1, you should consider upgrading to v0.19.2 at your earliest convenience.
Important
Planning to upgrade? Review instructions in the README.
In addition to our fix, we also removed some opinionated smooth scrolling behavior we used when navigating to fragments that we probably shouldn't have added in the first place.
What this means is, after upgrading to 0.19.2, if you have any pages in your site with internal links, navigation to them will now be instant, snapping immediately to the target element when you click a link. This is the browser's (and many sites') default behavior.
(For example, try clicking around on the section links in the sidebar of this Wikipedia page to see instant navigation in action.)
If you'd like to go back to re-enabling smooth scrolling in your site, we recommend adding this somewhere in your site, for example in your AppEntry.kt
file:
@InitSilk
fun enableSiteWideSmoothScrolling(ctx: InitSilkContext) = ctx.stylesheet.apply {
registerStyle("html") {
cssRule(CSSMediaQuery.MediaFeature("prefers-reduced-motion", StylePropertyValue("no-preference"))) {
Modifier.scrollBehavior(ScrollBehavior.Smooth)
}
}
}
What the above code does is set the site's scroll behavior to "smooth" unless some accessibility setting has been set on that user's machine indicating they don't want it. This approach is an encouraged best practice for enabling smooth scrolling.
Full Changelog: v0.19.1...v0.19.2
v0.19.1
Warning
This release added a logic bug around navigating to link fragments which was resolved in v0.19.2 (which you are encouraged to update to instead).
This release adds a few miscellaneous features and bumps up the Kotlin target to 2.0.20.
Important
Planning to upgrade? Review instructions in the README.
The biggest feature introduced in this release is callouts.
Note
GitHub also has callouts (they call them Alerts) and this is an example of one them: a note! GitHub also has alerts for tips, cautionary points, warnings, and important messages.
Kobweb includes all the alerts that GitHub exposes plus a few others.
I updated the handful of articles in my blog site to use callouts. If you'd like to see them in action, you can find a few examples of them in this post about fullstack Kobweb for example.
This release also adds markdown support for Callouts. They're as easy to create in Kobweb Markdown as they are in GitHub Markdown.
More information about Callouts and their markdown support can be found in the README.
Changes
Frontend
- Fixed an issue where working with
Modifier.displayIfAtLeast(Breakpoint)
and that family of methods would still result in elements flickering when a page first loaded. - Tweaked a handful of composable names to be more consistent with the official API Guidelines for Jetpack Compose.
- For example,
deferRender
->Deferred
, and a few others. - It's not expected that the average user will even notice, but this may result in some deprecation notices for some users.
- For example,
- Removed a timer-related hack which was used to scroll to page fragments.
- At the very least, the code is clean now; at best, there may have been the occasional case where the scroll operation wouldn't work and it should be a lot more robust now.
Silk
- New Callout widget
Markdown
- Added support for creating callouts
Gradle
- Fix a handful of cases in lesser used tasks that were still not configuration cache compatible.
Thanks!
In this release, we'd like to extend our appreciation to @okarmazin, who has been nothing but kind to the project in the Kotlin slack and has been going through the new user experience, filing bugs, and helping us by cleaning up mistakes and gaps in our README. Thank you very much for the support!
Full Changelog: v0.19.0...v0.19.1
v0.19.0
This release is identical to 0.18.3 but enables K2 support for Kobweb. 🎉
Please review the Kobweb K2 migration docs to learn how to migrate any existing projects over.
Important
Planning to upgrade? Review instructions in the README.
For the first time in several releases, we are seeing a decrease in the size of exported binaries. This may even be a more significant drop for projects that can use the new "ES2015" compiler target (see the migration docs for more details).
Note
For my own site, pre-0.19.0, the exported size was 1.12MB. In 0.19.0, it dropped to 1.00MB. And with ES2015, it dropped further to 941K. In total, a 16% drop without any changes to my code. That's pretty nice!
Also with this release, both Kobweb and all Kotlin Gradle plugins from JetBrains are configuration-cache compatible. If enabled for your project, you can experience significant compilation time speed-ups. This is highly encouraged given Kobweb's live-reloading experience.
Thanks!
This change exists thanks to @DennisTsar's efforts.
- He regularly tested Kobweb against pre-release versions of K2
- He identified and reported an incremental compilation issue with 2.0.0 (before things went out to our users!)
- He wrote the migration Gradle task and migration documentation
- He ensured that Kobweb was configuration-cache compatible by the time K2 came out
For my part, I mostly stayed out of his way, and I poked JB occasionally about the status of our filed bug.
This is a really incredible release, one that will result in memory and performance improvements for almost (or even every?) Kobweb user, and that many people were waiting for. And all the legwork for it happened while I was distracted by other tasks. I am truly grateful.
Of course, thanks as well to the Kotlin team. K2 is shaping up to be quite an excellent iteration on the language.
Full Changelog: v0.18.3...v0.19.0
v0.18.3
This release is a collection of miscellaneous features and bug fixes. Enjoy!
Important
Planning to upgrade? Review instructions in the README.
Changes
Frontend
- Modifier support for the
border-image
CSS property. - Added additional
margin
andpadding
-related CSS logical properties (like more overrides forscrollPadding
,marginInline
, etc.) - Fixed a bug where
Modifier.background(color)
without any other parameters would ignore the color value. - You can now programmatically query all routes in your site using the
PageContext
- e.g.
ctx.router.routes.forEach { r -> console.log(r.path) }
- e.g.
- Add new type-safe API for working with local storage and session storage (Documentation)
⚠️ Legacy route support has been removed.- See the Notes section for more information.
- Fixed some edge cases where underscores really confused route generation logic.
⚠️ Example_Route.kt
used to generateexample_route
and now will generateexample-route
.
Markdown
- Fixed issue where putting triple quotes in code blocks would generate code that wouldn't compile.
Backend
- API endpoints now support dynamic routes
- e.g.
@Api("articles/{article}")
- Access the value of
article
in the above case usingctx.params["article"]
- e.g.
Gradle
- Kobweb should now be compatible with the Gradle configuration cache.
- Note that the Kotlin plugin itself is not until v2.0. Kobweb should have support for this version soon.
Notes
Legacy Route support removed
If you don't care that much about a bit of Kobweb history, then don't read the rest of this section. Just go into your build.gradle.kts
and remove the line:
kobweb {
app {
// ↓↓↓↓↓↓↓
legacyRouteRedirectStrategy.set(DISALLOW) // DELETE ME!
// ↑↑↑↑↑↑↑
}
}
Caution
If you have this set to WARN
or ALLOW
, please read here.
However, if you'd like to learn more about this, read on!
Legacy route history
In the beginning, I wrote some logic to convert a Kotlin @Page
filename (Example.kt
) into a URL route (/example
). My initial algorithm simply lowercased everything, and while I did not realize it at the time, this made the default behavior generate names that did not conform to Google's SEO recommendations, which is to use hyphens to separate words.
In 0.16.0, we updated this! So ExampleRoute.kt
would now generate /example-route
and not /exampleroute
.
This long overdue change which, unfortunately, could break old sites if released directly.
Therefore, we did a bunch of work to intercept routes and handle legacy formats and new formats at the same time. In other words, a site with legacy route support would work if you visited either /examperoute
OR /example-route
.
However, it was always the plan to remove it sooner than later. It was a lot of ugly code we had to maintain, and the issue wasn't likely to affect that many users. Therefore, we gave users 6 months to migrate any offending cases over.
And that brings us to today! Legacy routes are finally out.
In order to not break compilation, we are leaving the legacyRouteRedirectStrategy
property in Gradle for now, but it doesn't do anything anymore (except print out a warning if you've set it). You have 6 months to delete this one-liner from your project...
Thanks!
We had a first time contribution from @FromWau who noticed some misformatted documentation and fixed it. These kinds of cleanups are truly appreciated! Thanks for helping keep Kobweb tidy.
And a serious shoutout to @DennisTsar who did all the hard work to fix my crappy Gradle code in order to make it configuration cache friendly.
Full Changelog: v0.18.2...v0.18.3
v0.18.2
This is a minor release mostly to fix an accidental API ambiguity error we introduced for some users in v0.18.1. We also picked up a few misc. additions along the way.
If v0.18.1 is working fine for you, there is no urgency to upgrade.
Important
Planning to upgrade? Review instructions in the README.
Changes
Frontend
- Fixed ambiguity error for people calling
Modifier.background(color)
without any other parameters. - Fixed an exception that would get fired if you used a design breakpoint inside a
ctx.stylesheet.registerStyle
block. - Added
mixBlendMode
modifier
Gradle
- Refactored our Gradle code more compatible with configuration caching.
Full Changelog: v0.18.1...v0.18.2
v0.18.1
This release is a grab bag of miscellaneous features, including the addition of the long requested spacedBy
arrangement method for Row
and Column
layouts (which comes thanks to a contribution from the community via @rafaeltonholo!). This release also features a handful of Markdown and CSS API fixes.
Important
Planning to upgrade? Review instructions in the README.
Note
Users are asking us when K2 support is going to land, but we are currently blocked due to a pretty major incremental compilation issue. You can learn more here and here. We are keeping a close eye on it and will support K2 as soon as we can!
Changes
Frontend
- New
spacedBy
arrangement support- See notes below for more information
- The
boxShadow
modifier can now take multiple shadow values - Add
transformBox
,transformOrigin
, andtransformStyle
modifiers - Add
overscrollBehavior
modifier.
Silk
-
Support for CSS layers
- Read more about the feature in the README
- Support for CSS layers was actually quietly introduced in 0.18.0 but not officially announced, since we wanted a bit of time to battle test the API first
-
You can now pass in a callback letting you configure the look and feel of TOC header links
-
Added new
descendants
andchildren
utility methods for use insideCssStyle
- For example,
CssStyle { children("div", "span") { /* ... */ } }
which is more expressive and easier to remember thancssRule(" > :is(div,span)")
- For example,
-
Link
andAnchor
widgets now accept an optionalupdateHistoryMode
parameter- This is useful if you want a link to replace the current page rather than visit it (in other words, if you want to change the behavior when you press the "back" button)
-
⚠️ RenamedonTextChanged
toonTextChange
andonValueChanged
toonValueChange
for relevant Silk widgest- This brings our naming in line with how Jetpack Compose names its callbacks. Apologies for catching it so late.
Markdown
- You can now nest Kobweb Call blocks, supporting Kobweb code calling Kobweb code.
- Fixed logic issue in markdown that was preventing markdown context from being created in certain situations.
- Fixed a case where multiline markdown text had newlines getting swallowed
Backend
- Cleaned up logic around detecting routes that represent routes vs. files
- For example, we no longer erroneously detect
1.0.0
inhttps://site.com/path/to/1.0.0
as a file with a.0
extension.
- For example, we no longer erroneously detect
Notes
spacedBy
In HTML / CSS, the normal way to put space between elements in a row, column, or table layout is using the gap CSS style, which we have actually recommended for the last two years. However, with spacedBy
, you can now use that instead:
// Using gap
Row(Modifier.columnGap(2.cssRem)) { /* ... */ }
// Using spacedBy
Row(horizontalArrangement = Arrangement.spacedBy(2.cssRem)) { /* ... */ }
The spacedBy
method can also take optional alignment parameters. Read the official Jetpack Compose documentation for more information about these new APIs.
Constructing Background
, Transition
, and Animation
CSS style properties
Over the past year, we have been building up a consistent vocabularly for how we want our CSS style classes to behave.
A very common pattern that has emerged is the CssStyleClass.of(...)
helper method, which is a useful way to construct many CSS style classes that have complex parameters.
For Background
, Transition
, and Animation
classes, however, since those classes already had (incomplete) definitions inside Compose HTML, we extended them by introducing our own CSSBackground
, CSSTransition
, and CSSAnimation
data classes.
We have since come to the conclusion that this is pretty inconsistent and confusing, so in this release, Kobweb has introduces its own Background
, Transition
, and Animation
classes. Constructing them is now consistent with all other CSS styles.
This may result in you getting deprecation warnings to migrate code over. Fixing it should be relatively straightforward, as in the following code:
-import com.varabyte.kobweb.compose.css.CSSTransition
+import com.varabyte.kobweb.compose.css.Transition
-CSSTransition("color", 50.ms))
+Transition.of("color", 50.ms))
Gradle
We have been hard at work getting Kobweb tasks to play nicer with the Gradle configuration cache. Our changes so far have all been implementation fixes that will be invisible to users, but we may need to deprecate / migrate some APIs in near future releases.
Thanks
This release we enjoyed first-time submissions from user @rafaeltonholo 🎉
They provided excellent PRs for the boxShadow
and spacedBy
changes. They also helped us identify inconsistencies in our codebase that we spent time tightening up.
Thank you Rafael for your contributions!
Full Changelog: v0.18.0...v0.18.1
v0.18.0
This release introduces the new CssStyle
concept, which supersedes and obsoletes the previous ComponentStyle
concept.
Important
Planning to upgrade? Review instructions in the README.
This release is one of the most significant changes we've made since we've started Kobweb development. These changes have been in discussion and development in the background for over eight months(!!!).
Caution
This release will likely introduce many deprecation warnings to your project. Please don't skip over the next section!
⚠️ Migrating your project
As authors of the framework, we're very excited about this release (and relieved to be finally getting it out there), but users should be prepared to hit anywhere from a handful to a bunch of warnings after upgrading. We've done everything we could to make the migration process as easy for users to do as possible, but we want to minimize surprise, which is why we're mentioning this up front.
If you are in the middle of a large change to your site, you probably want to hold off on upgrading until you can get your changes in.
After upgrading to Kobweb 0.18.0, users are encouraged to run ./gradlew kobwebMigrateToCssStyle
in a clean git branch.
For most projects, the automigration task should just work; in rare cases, some projects using more advanced features may run into errors which should be easily resolvable.
Users can read the full story about Cssstyle
here, including detailed migration support at the end of it.
A brief CssStyle
overview
The short version is, this old pattern:
val ExampleStyle by ComponentStyle {
/* ... */
}
has been replaced with
val ExampleStyle = CssStyle {
/* ... */
}
"That's it?" you might be thinking. "You're so excited about a name change?"
Well, getting rid of the by
keyword (a source of confusion for many new users) is already a nice win. But more importantly, we wrangled with a feature that organically grew out the constraints of its original design and fixed some of the longstanding issues with it.
Originally, ComponentStyle
was meant to be a mechanism just for Silk to use internally when defining widgets, but it became the common and recommended approach for Kobweb users everywhere to create styles, even if the styles they were creating had nothing to do with any specific component. (For example, Silk even did this itself! It provides the SmoothColorStyle
for smooth background color transitions; that's not really something that should be called a "component" style).
Note
In the context of Kobweb Silk, a style is Kotlin code that ultimately gets converted into / registered as a CSS stylesheet entry. Not too familiar with CSS stylesheet entries or why you should care about them? You can review the README section about them.
Moving forward, Kobweb now supports three broad categories of styles that we've identified across many users' projects: general styles, component styles, restricted styles.
General Styles
General styles are the most common type, and if you've been using ComponentStyle
in your codebase, probably 95-100% of those declarations fall into this category. This case is just a block of related, open-ended, useful style values.
val HeaderTextStyle = CssStyle.base {
Modifier.fontSize(32.px).fontWeight(FontWeight.Bold)
}
Starting in 0.18.0, you can now extend styles, which generate a new style that wraps its source style:
val TextStyle = CssStyle.base { Modifier.fontFamily("customfont") }
val HeaderTextStyle = TextStyle.extendedByBase {
Modifier.fontSize(32.px).fontWeight(FontWeight.Bold)
}
val LoudHeaderTextStyle = HeaderTextStyle.extendedByBase {
Modifier.color(Colors.Red).backgroundColor(Colors.Yellow)
}
With the above declared, LoudHeaderTextStyle.toModifier()
would apply itself and its dependent styles, setting font family, font size, and colors all in a single invocation.
Component Styles
Component styles are the ones we originally designed -- styles with a niche purpose to back some widget and allow users to generates variants (i.e. style tweaks) on top of them.
As a bonus, starting in 0.18.0, they are now typed, so that you can no longer use a variant created by one of these styles with another style.
sealed interface ProgressKind : ComponentKind
val ProgressStyle = CssStyle<ProgressKind> { /* ... */ }
val IndeterminiteProgressVariant = ProgressStyle.addVariant { /* ... */ }
@Composable
fun Progress(
modifier: Modifier = Modifier
variant: CssStyleVariant<ProgressKind>? = null
) {
val finalModifier = ProgressStyle.toModifier(variant).then(modifier)
Box(finalModifier) { /* ... */ }
}
Restricted Styles
Restricted styles, the final category, are a way to create a class that exposes a limited set of constructor parameters, generating a style behind the scenes. We call it restricted because, unlike the other types of style declarations which are totally open-ended, these styles are constrained by the handful of constructor parameters that the class owner exposes.
class ProgressSize(size: CSSLengthNumericValue) :
CssStyle.Restricted.Base(Modifier.size(size)) {
companion object {
val SM = ProgressSize(0.5.cssRem)
val MD = ProgressSize(1.cssRem)
val LG = ProgressSize(1.5.cssRem)
}
}
CssStyle document
This concludes a brief overview of the new feature, but to see more details, read this document.
Thanks
I'd like to take a moment to express an incalculable amount of thanks to @DennisTsar, without whom this feature wouldn't exist. He did a ton of foundational exploration and work, but more than that, he identifyied and raised concerns about ComponentStyle
in the first place.
I hope everyone working in open source is lucky enough to get to work with a core contributor of his generosity, good sense, and ability.
A final note
As library authors, we'd in general like very much not to make drastic changes like this, because we understand how disruptive it can be to our users who are just trying to get their work done.
Of course, it has been thanks to exposure from two years of some of the amazing projects that our users have created that has helped us improve our understanding of the domain. Normally, we can make incremental fixes here and there, but in this case, there was no way around ripping out the floor and installing a new one.
Still, we wanted to thank our users for their patience and understanding. We hope moving forward that people find CssStyle
more convenient to use, due to its shorter name (easier to type!), improved type-safety, and extended feature set.
Full Changelog: v0.17.3...v0.18.0
v0.17.3
This small release fixes two regression with routes introduced in v0.17.2 -- one subtle dealing with trailing slashes, and another significant one that broke dynamic routes.
Important
Planning to upgrade? Review instructions in the README.
The release also adds a handful of misc. features that are coming along for the ride.
Changes
Frontend
- Fixed regression where dynamic routes stopped capturing values correctly
- For example, the dynamic route
"/{folder}/{page}"
should match"/example/route"
providingparams["folder"] == "example"
andparams["page"] == "route"
- For example, the dynamic route
- Fixed regression where missing slashes wouldn't get auto-appended.
- For example, if you've registered the route
"/a/b/"
and the user visits"/a/b"
(no trailing slash), Kobweb will automatically suffix the"/"
for you at the end.
- For example, if you've registered the route
- Added tests for route logic so that we don't break subtle route features again...
- Added
:focus-within
psuedo-class - Updated FA icons to v6.5.2, which adds about a dozen or two new icons, including
FaThreads
for Meta's "Threads" logo.
Markdown
- Added support for surfacing the optional title of an image link, e.g.
![example](/some-image.png 'This is a title')
Full Changelog: v0.17.2...v0.17.3