KotlinBootstrap

Use the official Bootstrap UI components with Kotlin and Compose HTML, to build a frontend on the web.

Stars
186

Available Components

Bootstrap Icons usage

Install

Update a Project level build.gradle.kts file:

repositories {
    ..
    maven(url = "https://jitpack.io")
}

Update a site module build.gradle.kts file:

kotlin { 
    sourceSets {
        ..
        val jsMain by getting {
            dependencies {
                ..
                implementation("com.github.stevdza-san:KotlinBootstrap:0.1.5")
            }
        }
    }
}

Button

BSButton(
    text = "Sign in",
    onClick = {}
)
var buttonLoading by remember { mutableStateOf(false) }
BSButton(
    text = "Sign in",
    loading = buttonLoading,
    loadingText = "Please wait...",
    onClick = { buttonLoading = true }
)
BSButton(
    text = "Shopping Cart",
    badge = ButtonBadge(
        text = "10"
    ),
    onClick = {}
)
Column(modifier = Modifier.gap(20.px).fillMaxSize()) {
    Row(modifier = Modifier.gap(12.px)) {
        BSButton(
            text = "Apply Now",
            customization = ButtonCustomization(
                color = Colors.White,
                hoverColor = Colors.White,
                backgroundColor = Colors.Black,
                hoverBackgroundColor = rgba(0, 0, 0, 0.8),
                fontFamily = "Space Grotesk"
            ),
            onClick = {}
        )
        BSButton(
            text = "Get Started",
            customization = ButtonCustomization(
                color = Colors.White,
                hoverColor = Colors.White,
                activeColor = Colors.WhiteSmoke,
                borderColor = Colors.White,
                hoverBorderColor = Colors.White,
                activeBorderColor = rgb(168, 192, 255),
                gradient = linearGradient(
                    from = rgb(168, 192, 255),
                    to = rgb(63, 43, 150),
                    dir = LinearGradient.Direction.ToTopRight
                ),
                borderRadius = BSBorderRadius(all = 50.px),
                horizontalPadding = 1.25.cssRem
            ),
            onClick = {}
        )
    }
    Row(modifier = Modifier.gap(12.px)) {
        BSButton(
            text = "Submit",
            customization = ButtonCustomization(
                color = Colors.White,
                hoverColor = Colors.Wheat,
                activeColor = Colors.White,
                borderColor = Colors.White,
                hoverBorderColor = Colors.Wheat,
                activeBorderColor = Colors.White,
                gradient = linearGradient(
                    from = rgb(188, 78, 156),
                    to = rgb(248, 7, 89),
                    dir = LinearGradient.Direction.ToTopRight
                ),
                borderRadius = BSBorderRadius(topLeft = 20.px, bottomRight = 20.px),
                fontFamily = "Rubik"
            ),
            onClick = {}
        )
    }
}

IconButton

BSIconButton is a component used to display a Bootstrap Icon (BSIcons) inside a button. You can customize similar properties like with a regular BSButton as well. A basic usage:

Column(modifier = Modifier.gap(20.px).fillMaxSize()) {
    Row(modifier = Modifier.gap(12.px)) {
        BSIconButton(
            icon = BSIcons.UPLOAD,
            onClick = {}
        )
        BSIconButton(
            icon = BSIcons.UPLOAD,
            variant = ButtonVariant.PrimaryOutline,
            onClick = {}
        )
    }
    Row(modifier = Modifier.gap(12.px)) {
        BSIconButton(
            icon = BSIcons.ANDROID,
            variant = ButtonVariant.Success,
            onClick = {}
        )
        BSIconButton(
            icon = BSIcons.ANDROID,
            variant = ButtonVariant.SuccessOutline,
            onClick = {}
        )
    }
}

Input

var inputValue by remember { mutableStateOf("") }
BSInput(
    value = inputValue,
    placeholder = "Type here",
    onValueChange = {
        inputValue = it
    }
)
BSInput(
    value = inputValue,
    label = "Email Address",
    floating = true,
    onValueChange = {}
)
BSInput(
    value = inputValue,
    label = "Email Address",
    placeholder = "Type here",
    validation = InputValidation(
      isValid = true
    ),
    onValueChange = {}
)
BSInput(
    value = inputValue,
    label = "Email Address",
    placeholder = "Type here",
    validation = InputValidation(
      isInvalid = true
    ),
    onValueChange = {}
)
BSInput(
    value = inputValue,
    label = "Email Address",
    placeholder = "Type here",
    disabled = true,
    onValueChange = {}
)
BSInput(
    value = inputValue,
    label = "Email Address",
    placeholder = "Type here",
    plainText = true,
    onValueChange = {}
)

Dropdown

BSDropdown(
    placeholder = "Select a Platform",
    items = listOf("Android", "iOS", "Web"),
    onItemSelect = { index, value -> }
)
BSDropdown(
    items = listOf("Android", "iOS", "Web"),
    darkBackground = true,
    onItemSelect = { index, value -> }
)
BSDropdown(
    items = listOf("Android", "iOS", "Web"),
    disabledItems = listOf("iOS"),
    onItemSelect = { index, value -> }
)

TextArea

var value by remember { mutableStateOf("") }
BSTextArea(
    value = value,
    label = "Email Address",
    placeholder = "Type here...",
    onValueChange = { value = it }
)
var value by remember { mutableStateOf("") }
BSTextArea(
    value = value,
    label = "Email Address",
    floating = true,
    onValueChange = { value = it }
)

Checkbox

BSCheckbox(
    label = "Kotlin",
    onClick = {}
)
BSCheckbox(
    label = "C++",
    reverse = true,
    onClick = {}
)
BSCheckbox(
    label = "Python",
    toggleButton = true,
    onClick = {}
)

RadioButton

BSRadioButtonGroup {
    BSRadioButton(label = "Android", onClick = {})
    BSRadioButton(label = "iOS", onClick = {})
    BSRadioButton(label = "Web", onClick = {})
}
BSRadioButtonGroup(inline = true) {
    BSRadioButton(label = "Android", onClick = {})
    BSRadioButton(label = "iOS", onClick = {})
    BSRadioButton(label = "Web", onClick = {})
}
BSRadioButtonGroup(toggleButton = true) {
    BSRadioButton(label = "Android", onClick = {})
    BSRadioButton(label = "iOS", onClick = {})
    BSRadioButton(label = "Web", onClick = {})
}

Switch

BSSwitch(
    label = "Android",
    defaultChecked = true,
    onClick = {}
)
BSSwitch(
    label = "Android",
    disabled = true,
    onClick = {}
)

Alert

BSAlert(
    message = "Visit my YouTube Channel: Stevdza-San",
    icon = AlertIcon.Info,
    bold = "Stevdza-San"
)
BSAlert(
    message = "You have successfully purchased a book!",
    alertLink = Pair("book", "https://google.com"),
    icon = AlertIcon.Checkmark,
    style = AlertStyle.Success
)
BSAlert(
    message = "Dismissable Alert.",
    dismissible = true,
    style = AlertStyle.Dark
)

Toast

Even though a Toast component is not yet fully customizable, from this preview above you can see that there are different variations and styles that you can apply to it. For triggering a Toast component, you do need to call a special function showToast(toastId) and pass your toast id, in order to properly display it on the screen. Every Toast components needs to be wrapped inside the BSToastGroup composable. Also there's a ToastPlacement parameter available on BSToastGroup that you can use to modify a toast placement.

BSToast gets visible once you trigger a showToast() function:

BSToastGroup {
    BSToast(
        id = "toast",
        title = "Welcome",
        body = "Browse our website for more interesting products!",
        onCloseClick = {}
    )
}

BSButton(
    text = "Show Toast",
    onClick = {
        showToast("toast")
    }
)

BSToastBasic which is not automatically dismissable, because it has autoHide parameter equal to false:

BSToastGroup {
    BSToastBasic(
        id = "toastBasic",
        text = "Thank you for your feedback!",
        style = ToastStyle.Dark,
        autoHide = false,
        closeButtonDark = false,
        onCloseClick = {}
    )
}

BSToastAction which contains additional positive/negative buttons:

BSToastGroup {
    BSToastAction(
        id = "toastAction2",
        text = "Are you sure you want to delete 24 items?",
        positiveButtonText = "Yes",
        positiveButtonVariant = ButtonVariant.Primary,
        negativeButtonVariant = ButtonVariant.Danger,
        negativeButtonText = "Cancel",
        style = ToastStyle.Dark,
        onPositiveButtonClick = {},
        onNegativeButtonClick = {}
    )
}

Modal

BSModal component is not visible by default. If you want to show it on your page, then you need to call a modifier showModalOnClick() on a BSButton or any other clickable composable, and pass the ID of the modal itself. After you do that, just click the component that has that modifier, and your BSModal will appear.

BSModal(
    id = "contactModal",
    title = "Contact us",
    body = {
        Column {
            BSInput(
                modifier = Modifier
                  .fillMaxWidth()
                  .margin(bottom = 14.px),
                value = "",
                label = "Email Address",
                placeholder = "Type here...",
                onValueChange = {}
            )
            BSTextArea(
                modifier = Modifier.fillMaxWidth(),
                value = "",
                label = "Message",
                placeholder = "Type here...",
                onValueChange = {}
            )
        }
    },
    positiveButtonText = "Send Message",
    negativeButtonText = "Close",
    onPositiveButtonClick = {},
    onNegativeButtonClick = {}
)

BSButton(
    modifier = Modifier.showModalOnClick(id = "contactModal"),
    text = "Trigger",
    onClick = {}
)

Select

BSSelect(
    items = listOf("Android", "iOS", "Web", "Desktop"),
    placeholder = "Choose a Platform",
    onItemSelected = { index, value -> }
)
BSSelect(
    items = listOf("Android", "iOS", "Web", "Desktop"),
    placeholder = "Choose a Platform",
    floating = true,
    onItemSelected = { index, value -> }
)

Range

BSRange(
    modifier = Modifier.width(300.px),
    label = "Range (0-10)",
    min = 0,
    max = 10,
    onSelect = {}
)

Progress

BSProgress(percentage = 85.percent)
BSProgress(
  percentage = 85.percent,
  striped = true
)
BSProgress(
  percentage = 85.percent,
  stripedAnimated = true
)

Spinner

BSSpinner(variant = SpinnerVariant.Default)
BSSpinner(variant = SpinnerVariant.DefaultGrow)

Tooltip

Before you can use and display a Tooltip, you need to initialize them by calling initializeTooltips() function:

LaunchedEffect(Unit) {
    initializeTooltips()
}

Usually, the content on top of which you want to add a tooltip, is specified as a content lambda of the BSTooltip composable:

BSTooltip(
    text = "https://stevdza-san.com",
    content = {
        A(href = "https://stevdza-san.com") {
            SpanText(text = "Online Courses")
        }
    }
)

You can also change a direction of the tooltip, by using TooltipDirection parameter:

BSTooltip(
    text = "https://stevdza-san.com",
    direction = TooltipDirection.Right,
    content = {
        A(href = "https://stevdza-san.com") {
            SpanText(text = "Online Courses")
        }
    }
)

Collapse

To make your button or any other clickable component as the one that triggers the BSCollapse, you need to add a .showCollapse(id) modifier and pass the BSCollapse id.

Column(
    modifier = Modifier.width(400.px),
    horizontalAlignment = Alignment.CenterHorizontally
) {
    BSButton(
        modifier = Modifier
            .alignContent(AlignContent.Center)
            .showCollapse(id = "collapse1"),
        text = "FAQ",
        onClick = {}
    )
    BSCollapse(id = "collapse1") {
        Column(modifier = Modifier.margin(top = 14.px)) {
            SpanText(
                modifier = Modifier
                    .fontSize(18.px)
                    .fontWeight(FontWeight.Bold),
                text = "1. How long does the course take to complete?"
            )
            SpanText(
                text = """
                      The course is self-paced, so you can complete it at your own speed.
                      On average, most students finish the course in about 3-6 weeks, 
                      depending on the time they can dedicate to learning.
                """.trimIndent()
            )
        }
    }
}

Carousel

A basic usage of Carousel component:

BSCarousel(
    items = listOf(
        CarouselItem(
            image = "https://images.pexels.com/photos/2662116/pexels-photo-2662116.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
            title = "Moraine Lake"
        ),
        CarouselItem(
            image = "https://images.pexels.com/photos/147411/italy-mountains-dawn-daybreak-147411.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
            title = "Italy"
        ),
        CarouselItem(
            image = "https://images.pexels.com/photos/1166209/pexels-photo-1166209.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
            title = "Lavender"
        ),
    ),
    width = 900.px,
    height = 500.px
)

Breadcrumb

You can specify and replace a default divider parameter to change a separator string, and also you can set a currently selected BreadcrumbItem as well.

BSBreadcrumb(
    items = listOf(
        BreadcrumbItem(
            text = "Home",
            href = "#"
        ),
        BreadcrumbItem(
            text = "Pricing",
            href = "#"
        ),
        BreadcrumbItem(
            text = "Services",
            href = "#"
        ),
        BreadcrumbItem(
            text = "About",
            href = "#"
        ),
        BreadcrumbItem(
            text = "Contact us",
            href = "#"
        )
    ),
    divider = ">",
    currentItem = "About"
)

Accordion

You can customize it's flush parameter which will remove some borders and rounded corners to render accordions edge-to-edge with their parent container. alwaysOpen parameter will make accordion items stay open when another item is opened.

BSAccordion(
    modifier = Modifier.width(300.px),
    items = listOf(
        AccordionItem(
            title = "Step 01: Identify your goals",
            content = { SpanText(text = "Body text here...") },
            defaultOpened = true
        ),
        AccordionItem(
            title = "Step 02: Write your goals",
            content = {  SpanText(text = "Body text here...")}
        ),
        AccordionItem(
            title = "Step 03: Analysis",
            content = {  SpanText(text = "Body text here...")}
        ),
        AccordionItem(
            title = "Step 04: Objectives",
            content = { SpanText(text = "Body text here...")}
        )
    )
)

NavBar

The NavBar typically appears at the top of the web page and contains various navigation elements such as links, buttons, dropdown menus, and branding elements like logos or site names. It adapts to different screen sizes and devices, making it ideal for responsive web design.

BSNavBar(
    modifier = Modifier.fillMaxWidth(),
    stickyTop = true,
    itemsAlignment = Alignment.CenterHorizontally,
    brand = NavBarBrand(
        title = "KotlinBootstrap",
        image = "https://getbootstrap.com/docs/5.3/assets/brand/bootstrap-logo.svg",
        href = "#"
    ),
    expand = NavBarExpand.LG,
    backgroundStyle = BackgroundStyle.Dark,
    items = listOf(
        NavLink(
            id = "homeLink",
            title = "Home",
            onClick = {
                println("Index: $it Title: Home")
            }   
        ),
        NavLink(
            id = "servicesLink",
            title = "Services",
            onClick = {}
        ),
        NavLink(
            id = "pricingLink",
            title = "Pricing",
            onClick = {}
        ),
        NavLink(
            id = "aboutLink",
            title = "About us",
            onClick = {}
        ),
        NavDropdown(
            placeholder = "Language",
            items = listOf(
                NavDropdownItem(
                    id = "kotlinLanguage",
                    title = "Kotlin",
                    onClick = {
                        println("Index: $it Title: Kotlin")
                    }
                ),
                NavDropdownItem(
                    id = "javaLanguage",
                    title = "Java",
                    onClick = {}
                )
            )
        )
    ),
    inputField = NavBarInputField(
        placeholder = "Search",
        value = "",
        onValueChange = {}
    ),
    button = NavBarButton(
        text = "Search",
        onClick = {}
    )
)

You can add an extra parameter, to replace a default expandable menu with an Offcanvas side bar:

BSNavBar(
    ..
    offcanvas = NavBarOffcanvas(
        id = "myOffcanvas",
        title = "KotlinBootstrap",
        dark = true
    )
    ..
)

Offcanvas

Offcanvas is used to create sidebar or panel that can slide in and out of the viewport. This component is often used to display additional content, navigation menus, or options without taking up the entire screen space.

val links = listOf("Home", "Pricing", "Services", "Contact us")
BSOffcanvas(
    id = "myOffCanvas",
    title = "Welcome!",
    body = {
        Column {
            links.forEach { name ->
                A(
                    attrs = Modifier
                        .margin(bottom = 16.px)
                        .textDecorationLine(TextDecorationLine.None)
                        .cursor(Cursor.Pointer)
                        .toAttrs()
                ) {
                    SpanText(name)
                }
            }
            BSButton(
            text = "Sign in",
            onClick = {}
            )
        }
    },
    placement = OffcanvasPlacement.END
)

Column(
    modifier = Modifier.fillMaxSize(),
    verticalArrangement = Arrangement.Center,
    horizontalAlignment = Alignment.CenterHorizontally
) {
    BSButton(
        modifier = Modifier.showOffcanvasOnClick(id = "myOffCanvas"),
        text = "Show",
        onClick = {}
    )
}

Badge

There are four different BadgeVariant's: Straight, Regular, Rounded, Empty. You can customize the BackgroundStyle of the badge, a fontFamily, fontSize, and fontWeight as well.

Row(verticalAlignment = Alignment.CenterVertically) {
    SpanText(
        modifier = Modifier.margin(right = 8.px),
        text = "Fitness Tracker"
    )
    BSBadge(
        modifier = Modifier.margin(bottom = 8.px),
        text = "New",
        variant = BadgeVariant.Straight
    )
}

CloseButton

A basic usage:

BSCloseButton()

ColorPicker

A basic usage:

BSColorPicker(onColorSelected = {})

FilePicker

FilePicker component provides a required lambda onFileSelected, that returns two strings. The first one represents a fileName, while the second one the actual file encoded in BASE_64 string.

A basic usage:

BSFileInput(
    label = "Choose a file",
    onFileSelected = { fileName, file -> }
)

Pagination

BSPagination component is used to divide long lists or tables into multiple pages, making it easier for users to navigate through the content.

A basic usage:

var currentPage by remember { mutableStateOf(1) }

BSPagination(
    pages = 15,
    maxVisiblePages = 3,
    currentPage = currentPage,
    previousButton = PreviousButton(
        onClick = { currentPage = it }
    ),
    nextButton = NextButton(
        onClick = { currentPage = it }
    ),
    onPageClick = { currentPage = it }
)

Icons

There are over 2.000 icons available in a Bootstrap library. You can use a BSIcons object to access all icons. There's a BSIcon composable function that allows you to display those same icons as well. There's also a BSIconButton composable that displays an icon inside the button.

Example (Zoomed in) icon, that represents one of the many vector icons in the library:

BSIcon(
  icon = BSIcons.ANDROID,
  size = 1.cssRem,
  color = Colors.LightGreen
)