[00:00.000 --> 00:08.160] So, do you have an answer to that question? [00:08.160 --> 00:12.600] Okay, don't worry, I have some answers. [00:12.600 --> 00:16.080] And so we'll start with the wrong answers, of course. [00:16.080 --> 00:25.520] Because Kotlin is cool, because of the hype, because we love to hate JavaScript. [00:25.520 --> 00:31.400] First JavaScript was designed in just seven days, while Kotlin was designed in six years. [00:31.400 --> 00:38.000] It's not completely false, but I don't think it's super accurate, because JavaScript kept [00:38.000 --> 00:44.440] evolving and Kotlin as well, and JavaScript is still older than Kotlin, anyway. [00:44.440 --> 00:47.120] So what can we do on the web with Kotlin? [00:47.120 --> 00:53.080] I think it's important to answer that question first, because, well, it's Kotlin.js, so it [00:53.080 --> 00:55.000] still has some JS inside. [00:55.000 --> 00:58.760] So can we do as much as we can do with JS? [00:58.760 --> 01:05.440] And so I want to show you a demo of that website, and hopefully the switch of the tabs will [01:05.440 --> 01:07.400] happen smoothly. [01:07.400 --> 01:08.400] Give me a second. [01:08.400 --> 01:09.400] Oh crap. [01:09.400 --> 01:12.400] Forgot to add internet. [01:12.400 --> 01:32.400] How in 20, 20, 30 people still forget to connect to internet, give me 10, 20 seconds to go. [01:32.400 --> 01:38.280] So this website has been done with Kotlin.js, so it's a place where you can play with data [01:38.280 --> 01:39.280] to use. [01:39.280 --> 01:43.240] That's the name of our company. [01:43.240 --> 01:45.680] And so it's about the data visualization. [01:45.680 --> 01:49.760] Some of you maybe know D3JS, who knows D3JS? [01:49.760 --> 01:55.960] Okay, so basically data to use, I mean, there's an open source repository named data to use, [01:55.960 --> 01:56.960] same as the company. [01:56.960 --> 02:03.400] This is D3JS, but for Kotlin, and it doesn't compile just for JavaScript, but also for [02:03.400 --> 02:09.040] other platforms that Kotlin supports, you will see which platforms Kotlin support. [02:09.040 --> 02:19.880] And so you can do this kind of thing that look like those. [02:19.880 --> 02:27.400] So this, I wanted to show you a few examples, dynamic example, because this was just a web [02:27.400 --> 02:31.520] page, and just a web page is not very interesting nowadays. [02:31.520 --> 02:36.640] So this is entirely made with Kotlin.js, please. [02:36.640 --> 02:37.640] What is happening? [02:37.640 --> 02:48.880] Should respond to, yeah, so you see it's reacting with forces, can fix a big one, we'd have a [02:48.880 --> 02:49.880] better effect. [02:49.880 --> 02:55.440] So this was done with Kotlin.js with the calculations and the rendering. [02:55.440 --> 03:03.520] There's also this one, rip curtains. [03:03.520 --> 03:10.280] Didn't like switching density for monitors, but now it's smooth again. [03:10.280 --> 03:19.200] Okay, really uncomfortable to have two different screens. [03:19.200 --> 03:32.920] The one, same, but with some color. [03:32.920 --> 03:42.200] Okay, anyway, now I also want to show you something built on top of data to use, which [03:42.200 --> 03:43.200] is Charles's Katie. [03:43.200 --> 03:46.200] Unfortunately, Charles's Katie is not open source, but the examples are. [03:46.200 --> 03:56.480] I know we are for them, so it's a bit, how can I say, the team line sometimes, but yeah, [03:56.480 --> 04:01.120] this is the kind of thing, so it's a charting average, just basically with as little code [04:01.120 --> 04:04.600] as possible, we want to have a chart that makes sense with the right bounds, with the [04:04.600 --> 04:07.280] right zooming, and all these kind of things. [04:07.280 --> 04:11.360] There are also a lot of charts, but you will find them in the presentation. [04:11.360 --> 04:12.360] The links are clickable. [04:12.360 --> 04:14.560] I will share the link at the end. [04:14.560 --> 04:17.240] Okay, so what can we do on the web? [04:17.240 --> 04:21.600] A lot of complicated things, just like JavaScript, you can manipulate the DOM, go on canvas, [04:21.600 --> 04:25.240] write reliable and shareable business logic. [04:25.240 --> 04:28.760] There's no hard limit. [04:28.760 --> 04:34.440] And so now why, actual answers, why we did JavaScript or code.js for this, and other [04:34.440 --> 04:38.720] projects as well, sometimes I should say. [04:38.720 --> 04:45.200] So the first reason was safety and reliability, especially when working with multiple people [04:45.200 --> 04:47.200] over time. [04:47.200 --> 04:52.120] We don't need to transfer a lot of knowledge because it's already built into the code itself [04:52.120 --> 04:58.360] thanks to typing, for example, the types, like this is a number, this is a string, this [04:58.360 --> 05:03.080] is whatever object we have, maybe this is a duration. [05:03.080 --> 05:08.520] Duration is not just an integer or a long, it's a duration, so we know what we are dealing [05:08.520 --> 05:15.040] with, the language design, the concise syntax, because that's also something we like in JavaScript [05:15.040 --> 05:21.440] and we like a bit less in Java, for example, having a concise syntax is actually important, [05:21.440 --> 05:27.960] type safety, concelable async programming, also named coroutines, and other features [05:27.960 --> 05:32.800] in the language design, but also code sharing. [05:32.800 --> 05:38.120] So we can share what we want and we can have native interoperability with a platform, say [05:38.120 --> 05:43.680] at some point we want to make a mobile app, we can actually compile the same way all the [05:43.680 --> 05:49.280] native apps are made with the same code, granted that we are not using JavaScript or web-only [05:49.280 --> 05:51.880] APIs. [05:51.880 --> 05:56.640] And there are some APIs that work the same, for example, plus adding two numbers, it works [05:56.640 --> 06:01.480] on all platforms, and there are a lot of other things that work on all platforms, actually. [06:01.480 --> 06:09.040] So let's focus on code sharing first, because sharing is caring, right, that's what some [06:09.040 --> 06:10.040] people say. [06:10.040 --> 06:15.920] And so I want to answer which platforms does Kotlin run on or compile to, I should say, [06:15.920 --> 06:22.400] because Kotlin, in a way, unfortunately, but at the same time, it's so strange, is a compile [06:22.400 --> 06:26.760] language, which means that you might need to wait a bit before you can actually test [06:26.880 --> 06:28.880] your code. [06:28.880 --> 06:36.400] Yeah, so it compiles for the JVM, that means server, desktop, apps, libraries, JavaScript, [06:36.400 --> 06:44.760] browser, Node.js as well, Android, and whereas in what is within the Android runtime, which [06:44.760 --> 06:53.600] is kind of a JVM, but not really, and native, so kind of like C++, you can compile Kotlin [06:53.600 --> 07:02.760] for iOS, watchOS, I don't have an Apple watch, but you get it, macOS, CVOS, Linux, Windows, [07:02.760 --> 07:08.120] and also the Android NDK, which is kind of outside the Android runtime, the kind of JVM [07:08.120 --> 07:14.360] art, you know, you can also compile there, if you want, can be useful if you make a video [07:14.360 --> 07:16.600] game, for example. [07:16.600 --> 07:22.640] So basically Kotlin runs natively on almost all devices, at least devices where we run [07:22.640 --> 07:32.200] what we call apps, so maybe not a microcontroller, but if it's slightly more powerful, yes, but [07:32.200 --> 07:43.640] Kotlin is not all about Kotlin, actually, interoperability is a key thing in Kotlin, [07:43.640 --> 07:48.320] and so Kotlin has direct interoperability, which means that you don't have to use a special [07:48.320 --> 07:51.960] API where you pass a string for the name of the method and then the list of arguments, [07:51.960 --> 07:54.640] you just call it like it was made in Kotlin. [07:54.640 --> 07:58.720] So it has direct interoperability with Java, with JavaScript. [07:58.720 --> 08:04.840] Now with TypeScript as well, very recently, in the last release of Kotlin, a few weeks [08:04.840 --> 08:16.080] ago, Objective-C, and therefore Swift and C, but unfortunately there are quirks, because [08:16.080 --> 08:22.200] languages have specific design and sometimes it's hard to have the square fit into the [08:22.200 --> 08:32.560] square-ish, so it works both ways for most of the languages, but sometimes you are limited. [08:32.560 --> 08:37.480] Just want to be completely honest about that, for Java it works well, actually I should [08:37.480 --> 08:43.920] put Java in orange as well, because a few features now don't work supernaturally with [08:43.920 --> 08:48.440] Kotlin, you have to jump to slide hoops when it comes to async programming. [08:48.440 --> 08:55.800] If you do it the best way in Kotlin, you cannot apply that in Java, but you will be able to [08:55.800 --> 09:00.520] support all the results that you've got yourself. [09:00.520 --> 09:05.760] What direct interoperability means is that also you are not reliant on third-party plug-in [09:05.760 --> 09:09.760] updates, for example, it could be Cordova or Flutter, you are waiting for them to support [09:09.760 --> 09:14.200] the new release of something or that new standard in web, you don't need that in Kotlin, because [09:14.200 --> 09:19.200] you are still doing a native app, but just you don't have to write to duplicate the code [09:19.200 --> 09:24.400] for each platform, it's like you are doing JavaScript if you use it for the web, so in [09:24.400 --> 09:30.000] JavaScript, no, it doesn't need to compile for JavaScript. [09:30.000 --> 09:35.680] You can use native APIs like they were built in Kotlin, so calling new Kotlin code from [09:35.680 --> 09:41.720] Java, like it was built in Java, the same for Swift, same for JavaScript. [09:41.720 --> 09:47.720] Sometimes there are limitations, as I told you, but for most of the use cases, it works [09:47.720 --> 09:55.880] just like this and sometimes it's only a minor thing and you will find the answer on the internet. [09:55.880 --> 10:00.880] Kotlin nowadays is the most interoperable programming language, unlike C++, which doesn't [10:00.880 --> 10:09.440] compile for JavaScript, for example, and I know C++ was used for multi-platform libraries, [10:09.440 --> 10:17.400] even though I don't really want to deal with overflows on a daily basis, personally. [10:17.400 --> 10:24.160] And so back to that slide, now we look at safety and reliability, because, oh, okay, [10:24.160 --> 10:26.640] let's move on. [10:26.640 --> 10:33.120] So for me, it's reason number one. [10:33.120 --> 10:40.040] I don't know if it speaks to you, but your user might be in display. [10:40.040 --> 10:41.520] And so that's how JavaScript is like. [10:41.520 --> 10:42.520] It doesn't care. [10:42.520 --> 10:48.080] It's like it's on you, check whether you still have the wing. [10:48.080 --> 10:49.080] Missing both? [10:49.080 --> 10:50.080] Wing off? [10:50.080 --> 10:51.080] No problem. [10:51.080 --> 10:52.080] Let's try to fly it anyway. [10:52.080 --> 10:54.640] Let's try to run it anyway. [10:54.640 --> 11:00.760] That also applies to other programming languages like Python, Bash, all PHP, not PHP 7, but [11:00.760 --> 11:08.720] yeah, and basically languages that say, yeah, types, we don't need them or when you want. [11:08.720 --> 11:17.080] So on the other side, Kotlin is more like this, bolt required, no takeoff possible, wrong [11:17.080 --> 11:24.440] bolt type, no takeoff possible, expect the pilot seat but got an economic seat. [11:24.440 --> 11:26.080] No takeoff possible. [11:26.080 --> 11:31.480] So you don't have to fly the plane to realize that one wing wasn't attached properly or that [11:31.480 --> 11:40.760] you were missing the wheels for landing because you want to realize that before taking off. [11:40.760 --> 11:46.320] And that also means that compiling is not just about browsing the web while waiting. [11:46.320 --> 11:48.320] Compile languages mean less tests to write. [11:48.320 --> 11:52.320] You don't have to check whether the input is of the right kind. [11:52.320 --> 11:58.240] You only have to check for the values but not for like, someone gave me a horse but [11:58.240 --> 12:02.480] I was expecting an integer. [12:02.480 --> 12:04.400] So it can save you time. [12:04.400 --> 12:06.880] But not at the time you might expect. [12:06.880 --> 12:08.400] That's the quirk. [12:08.400 --> 12:12.720] Anyway, so what should we do on the web with Kotlin? [12:12.720 --> 12:18.400] Because in my opinion, it doesn't suite all use cases, a lot of them but not all. [12:18.400 --> 12:23.600] So first web apps, when I mean web apps, I mean like something where you are not just [12:23.600 --> 12:29.640] going to look at one page but something where you are going to do some interaction, say [12:29.640 --> 12:35.280] watch a video ad, oh no. [12:35.280 --> 12:39.800] Web pages with a lot of dynamic logic that can't move to server side. [12:39.800 --> 12:45.640] Kotlin might be a good alternative to do that. [12:45.640 --> 12:53.280] Because when you have a lot of logic, types and correctness is even more important. [12:53.280 --> 12:59.240] So you actually, personally I feel less stressed when I do complex logic with programming language [12:59.240 --> 13:02.360] that checks a lot of things for me. [13:02.360 --> 13:05.320] Anything that doesn't have to be less than 300 kilobytes. [13:05.320 --> 13:10.440] If you have video ads, you don't need to be less than 300 kilobytes. [13:10.440 --> 13:14.640] Otherwise, if it's a landing page, maybe it's not the best solution, maybe you want to generate [13:14.640 --> 13:17.760] your web page, maybe even with Kotlin code. [13:17.760 --> 13:24.040] But you don't really want to have a bundle compiled with Kotlin on your landing page. [13:24.040 --> 13:27.920] But anything further than a landing page, or if your landing page is heavy or all your [13:27.920 --> 13:34.040] users have a fast internet, you don't really care, network is cheap when you are lucky [13:34.040 --> 13:35.040] these days. [13:35.040 --> 13:38.560] So that's when you're lucky, think about it. [13:38.560 --> 13:42.960] You can generate HTML with Kotlin, as I told you, with Kotlin JVM, it's actually the simplest [13:42.960 --> 13:43.960] way to do it. [13:43.960 --> 13:51.600] But there's an API, Kotlin X.html that works on both JVM and JS. [13:51.600 --> 13:58.480] You can also do libraries for Kotlin consumers, complex libraries for TypeScript and JavaScript [13:58.480 --> 14:01.480] consumers also. [14:01.480 --> 14:06.200] This is a good fit for Kotlin. [14:06.200 --> 14:12.560] And now I want to talk about language design, because I told about Kotlin, but who of you [14:12.560 --> 14:17.360] have ever seen the syntax of Kotlin? [14:17.360 --> 14:18.360] Who hasn't? [14:18.360 --> 14:19.360] Yeah. [14:19.360 --> 14:20.360] Okay. [14:20.360 --> 14:21.360] Anyway. [14:21.360 --> 14:25.320] A lot of curious people in this room. [14:25.320 --> 14:28.640] So basically, if you want to have a variable, you can just write it like this. [14:28.640 --> 14:31.800] You see, we don't declare the type, because we don't need this obvious. [14:31.800 --> 14:36.480] It's an integer, 23, what else could it be? [14:36.480 --> 14:44.120] If you want to increment it like this, it won't compile, because it's an immutable reference, [14:44.120 --> 14:45.120] Val. [14:45.120 --> 14:52.600] As opposed to Val, variable, you can change the value like this, and it will increment. [14:52.600 --> 14:55.080] You have functions. [14:55.080 --> 15:03.080] You write the keyword from, and here if I pass, I don't know, first them to name, then [15:03.080 --> 15:08.640] it will have welcome back first them, a few years after. [15:08.640 --> 15:12.320] And we can actually put it on one line if you want, if it's short enough. [15:12.320 --> 15:16.080] So that's great for our conciseness. [15:16.080 --> 15:21.720] Sometimes you just want to give a name to a dumb operation, but you want it to be more [15:21.720 --> 15:27.640] clear in the code, and we spend more time reading code than writing code usually. [15:27.640 --> 15:32.200] You can also omit the type if it can be inferred from the expression after it. [15:32.200 --> 15:36.040] Only when it's a one-liner, even though you could have something that has some curly braces, [15:36.040 --> 15:39.280] it has to be like an entire expression. [15:39.280 --> 15:45.240] You cannot have multiple expressions or statements if you want to omit the types. [15:45.240 --> 15:46.240] You can make a class. [15:46.240 --> 15:47.800] This actually compiles. [15:47.800 --> 15:50.800] If you want to put something in it, you can add parenthesis. [15:50.800 --> 15:52.840] And now it's a data holder. [15:52.840 --> 15:58.200] Usually we write it like this for indentation, but it's not mandatory. [15:58.200 --> 15:59.200] It will still compile. [15:59.200 --> 16:02.840] It's not Python common. [16:02.840 --> 16:05.680] You can also add the data keyword. [16:05.680 --> 16:10.960] So the data keyword can come handy when you want to have the equals implementation based [16:10.960 --> 16:15.320] not on the identity of the object, but on the content. [16:15.320 --> 16:21.680] Say if you use a hash map, for example, then if you make a copy, but it's still exactly [16:21.680 --> 16:25.480] the same, somehow you make a copy in your program because it's complex, then the key [16:25.480 --> 16:26.480] will match. [16:26.480 --> 16:31.720] But if you don't use data and you still want that behavior, you have to implement or let [16:31.720 --> 16:34.000] the IDE implement the equals function. [16:34.000 --> 16:38.720] There's also hash code that speeds up dictionary operations. [16:38.720 --> 16:41.800] And there's two strings when you want to log the content. [16:41.800 --> 16:48.000] So data keyword, you just add this, and the compiler implements the thing. [16:48.000 --> 16:49.000] You can make it open. [16:49.000 --> 16:52.600] By default, they are not open, the classes, so you cannot subclass them because usually [16:52.600 --> 16:59.800] people didn't really want their class to be subclass, so yeah. [16:59.800 --> 17:03.320] And then you can have a subclass like this. [17:03.320 --> 17:07.360] Then this is how you can implement an interface and override a function. [17:07.360 --> 17:12.240] If it was comparable, if it was a class, you would have parenthesis next to it, but otherwise [17:12.240 --> 17:15.960] it's exactly the same syntax, same principle of inheritance. [17:15.960 --> 17:20.760] There's the handy to-do function when you want your code to compile, but you are not [17:20.760 --> 17:21.760] done yet. [17:21.760 --> 17:27.720] So if it actually runs that function, it will crash, but maybe you can see if the plane [17:27.720 --> 17:29.640] is able to take off. [17:29.640 --> 17:34.640] And then it's very easy to find back the to-do calls because you control or command click [17:34.640 --> 17:35.640] on to-do. [17:35.880 --> 17:38.680] It will take you to the declaration, and if you do it back at the declaration site, it [17:38.680 --> 17:42.160] will show you all the usages in your project. [17:42.160 --> 17:50.720] A few cool language tutorials, null safety, nullable types, and null pointer exception [17:50.720 --> 18:00.440] safe equals, so it's kind of type error when you try to add an integer, but it's not initialized. [18:00.440 --> 18:03.640] And for example, this doesn't compile because string is not nullable. [18:03.640 --> 18:08.080] If you want it to be nullable, you have to add the question mark here. [18:08.080 --> 18:10.720] And now this can compile. [18:10.720 --> 18:16.880] But this doesn't compile, again, because greeting might be null, and actually you set it to [18:16.880 --> 18:17.880] null. [18:17.880 --> 18:21.920] And even if you remove that, it still doesn't compile because you explicitly stated that [18:21.920 --> 18:25.720] the type was nullable. [18:25.720 --> 18:30.920] But this, with the question mark before the call, it becomes its name is safe call, and [18:30.920 --> 18:31.920] this compiles. [18:31.920 --> 18:38.480] If there's no value, if string is put to null, then you won't get null instead, or it will [18:38.480 --> 18:41.880] just skip the function call. [18:41.880 --> 18:46.520] You can also crash a program in case it wasn't null, so in this case, it will work fine because [18:46.520 --> 18:49.520] hello world is there. [18:49.520 --> 18:54.800] But if you add null, it compiles again, but it crashes at runtime. [18:54.800 --> 18:57.400] But you wanted it, right? [18:58.400 --> 19:07.240] This doesn't compile because the type is int, but it returns int question mark. [19:07.240 --> 19:09.800] But you can also use the Elvis operator. [19:09.800 --> 19:14.920] If you turn it 90 degrees, it looks a bit like Elvis haircut. [19:14.920 --> 19:20.760] And so when the first part is null, then you get the expression after. [19:20.760 --> 19:26.600] Don't overuse it, but it's quite cool for search cases. [19:26.600 --> 19:33.960] The equals, equals checks for equals, and doesn't check for identity, like in Java. [19:33.960 --> 19:38.880] So this is similar to this code in Java if you compile for the JBM. [19:38.880 --> 19:42.480] And if you add a triple equals, then it's comparing the identity. [19:42.480 --> 19:45.560] And in Java, that's a double equals. [19:45.560 --> 19:47.760] We think it makes more sense the way it's done in Kotlin. [19:47.760 --> 19:53.080] I think it's very similar to what's done in JavaScript, in a way. [19:53.080 --> 19:55.560] And if both values are null, it doesn't crash. [19:55.560 --> 19:57.600] It won't try to call equals on a null object. [19:57.600 --> 19:59.320] It will see, oh, both are null. [19:59.320 --> 20:00.320] Okay. [20:00.320 --> 20:03.400] One is null, one is not equal. [20:03.400 --> 20:07.000] Seems more intuitive when you say it like this, I think. [20:07.000 --> 20:08.080] Other things are properties. [20:08.080 --> 20:11.800] So val and var, if you put it in a class, those are properties. [20:11.800 --> 20:14.360] Otherwise, they are local properties if they are not in a class. [20:14.360 --> 20:18.480] And so they are similar to fields, but they also include getters and setters. [20:18.480 --> 20:20.120] And they can be delegated. [20:20.120 --> 20:23.440] You can Google for Kotlin delegated properties if you want to find out more. [20:23.440 --> 20:29.840] But basically, it helps you to not repeat the get and set on and on. [20:29.840 --> 20:32.280] So properties, these are properties. [20:32.280 --> 20:37.280] You can specify the type or not, then it infers whatever the expression is. [20:37.280 --> 20:40.720] This is a custom getter and setter. [20:40.720 --> 20:45.680] And if you type get or set in the ID, it will suggest you the right curly braces to make [20:45.680 --> 20:46.920] it work. [20:46.920 --> 20:51.680] And there's also extension functions, which are static methods, kind of like instance [20:51.680 --> 20:54.280] method when you call them, but you don't need to edit the class. [20:54.280 --> 21:00.240] So it works on any type, including integer, which is built in Kotlin, right, into whatever [21:00.240 --> 21:01.760] platform is behind. [21:01.760 --> 21:06.480] So this allows you to have ease even on any integer where the distinction is visible or [21:06.480 --> 21:10.040] imported. [21:10.040 --> 21:13.720] You can use it like this, three dot ease even, it works. [21:13.720 --> 21:15.440] I think I'm out of time. [21:15.440 --> 21:17.320] Maybe I have one extra minute. [21:17.320 --> 21:23.800] Okay, few seconds. [21:23.800 --> 21:29.360] I want to show higher order functions, so you can pass a function. [21:29.360 --> 21:32.120] And I guess some of you are familiar with lemdas. [21:32.120 --> 21:35.840] So higher order functions are functions that take lemdas. [21:35.840 --> 21:39.080] And in Kotlin, they can be in line. [21:39.080 --> 21:41.240] And so this is a lemda. [21:41.240 --> 21:43.800] This is a lemda, so it's just curly braces. [21:43.800 --> 21:47.720] And whenever you see curly braces, it's not just a function, it's a lemda. [21:47.720 --> 21:54.160] You can also check types, but you don't have to cast again, like you would in Java. [21:54.160 --> 21:58.280] And string templates, if you want to use a variable, you don't have to do concatenation. [21:58.280 --> 22:02.160] You just use dollar and put the name of the variable. [22:02.160 --> 22:05.040] Anyway, the entire content is here. [22:05.040 --> 22:07.280] I think I said the most important thing. [22:07.280 --> 22:15.880] You can flash that QR code and go on speakerdeck.com, my handle, and you will find the presentation [22:15.880 --> 22:22.760] with all the links, including some links if you want to get started if you never try Kotlin. [22:22.760 --> 22:25.760] Thank you. [22:25.760 --> 22:32.360] So we have time for one or two questions. [22:32.360 --> 22:37.240] And I'll ask the first question, which is for the next speaker. [22:37.360 --> 22:39.360] Are you here? [22:39.360 --> 22:41.360] Okay, so you don't have to move. [22:41.360 --> 22:44.360] Next question then. [22:44.360 --> 22:47.360] You raise your hand, I run to you with the mic. [22:47.360 --> 22:57.360] I can't dust it because it's dangerous. [22:57.360 --> 23:03.360] Okay, so you mentioned the Kotlin X HTML, right? [23:03.360 --> 23:06.360] So is that something like for CSS? [23:06.480 --> 23:13.480] I think there's like Kotlin X CSS for extensions also for JavaScript. [23:13.480 --> 23:17.480] Yeah, CSS, it's a bit of a complicated topic. [23:17.480 --> 23:18.480] There are multiple ways. [23:18.480 --> 23:23.480] You can do it the way you do with plain HTML and you have a link ref. [23:23.480 --> 23:28.480] But there's also some special support where you can compile a SAS into CSS. [23:28.480 --> 23:32.480] Or you can also write Kotlin that translates to CSS. [23:32.480 --> 23:36.480] But I think it's runtime CSS. [23:36.480 --> 23:41.480] I think the best way to answer the question will be to Google for Kotlin CSS. [23:41.480 --> 23:44.480] Because I don't have the perfect answer in my head. [23:44.480 --> 23:48.480] But yeah, there are some things about that. [23:48.480 --> 23:51.480] Another question? [23:51.480 --> 23:54.480] Thank you. [24:02.480 --> 24:05.480] Thank you.