Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bootstrap Web Components #28131

Closed
thepassle opened this issue Jan 28, 2019 · 24 comments
Closed

Bootstrap Web Components #28131

thepassle opened this issue Jan 28, 2019 · 24 comments
Labels

Comments

@thepassle
Copy link

thepassle commented Jan 28, 2019

Hi,

I was wondering if there have been any discussions on releasing a set of bootstrap web components; they seem to be the perfect use case for something like bootstrap.

Web Components are a set of standards that allows developers to make reusable, encapsulated and modular components using standards based APIs. More so, web components are reusable in almost any framework.

With the recent anouncement that support for IE11 will be dropped, polyfills won't even be necessary; it seems like the perfect time.

Instead of applying classes, bootstrap web components could be usable like so:

<bootstrap-button></bootstrap-button>
<bootstrap-button raised></bootstrap-button>
<bootstrap-button raised warning></bootstrap-button>
<bootstrap-button raised warning large></bootstrap-button>

etc.

There is already a community effort by @morbidick :
https://github.com/morbidick/bootstrap-webcomponents

Some examples of existing web component catalogs are:

screen shot 2019-01-28 at 22 15 28

screen shot 2019-01-28 at 22 15 46

If you're interested in learning more, you can find a ton of information at:

Recommendations/tooling:

As well as these blogs:

@dvmuccillo
Copy link

I was wondering about it too, would be awesome to have this.

But based on the browsers that Bootstrap 5 will likely support and the need to polyfill things (a lot?), if things go on this way it'll be on a v6 realese IMO.

Also there is the main focus of trying to solve things with css whenever it's possible, so i'm curious on how things would go.

@web-padawan
Copy link

Thanks for starting the discussion. I recall lots of tweets of this kind under Bootstrap v5 announcements. Dropping IE11 is a good opportunity to introduce web components, too.

It has its drawbacks and edge cases, though (simple case: :slotted CSS selector is quite limited, e.g. for styling links in alerts it will require to make them direct children).

@stramel
Copy link

stramel commented Jan 28, 2019

I have wanted to throw together a bunch of bootstrap webcomponents for the past 3 years. I finally decided to play around with it a bit this last week.

This is roughly what I came up with. I'm not sure how much flexibility the BS team is looking for on their components. I focused on maintaining a very similar API as what currently exists. (Haven't fully fleshed out the CSS or all the variants CSS vars)

https://stackblitz.com/edit/bs-alert?file=my-element.ts

@Johann-S
Copy link
Member

I agree WebComponents can suits very well to Bootstrap, but our v5 is a huge move for us (due to the removal of jQuery) so we won't begin that in our v5, but maybe in a v6 that's possible

@DanL
Copy link

DanL commented Feb 17, 2019

WebComponents would make Bootstrap super compelling for me; I've always avoided it because of the fact that you need to opt-in for the entire page, rather than picking-and-choosing what's useful.

@cvrebert
Copy link
Collaborator

cvrebert commented Mar 4, 2019

Old duplicates: #18015, #14200

@web-padawan
Copy link

For someone interested: I have started working on a small library (5 components at the moment) which in particular provides the Web Components with CSS inspired by Bootstrap 4.

I have changed a lot, in order to use custom CSS properties for theme colors and enable automatic contrast adjustments, using hsl() and calc(). So that's quite a significant rewrite.

Demo: https://web-padawan.github.io/aybolit/
Source: https://github.com/web-padawan/aybolit/tree/master/packages/bootstrap

@cyberhck
Copy link

@Johann-S , hey you mentioned removal of jQuery for v5, can you point me for more info? will all the jQuery based component have separate JS function which will trigger the init instead? that'd be awesome, then I could finally use Bootstrap with react properly, that's certainly a good news.

@Johann-S
Copy link
Member

Hi @cyberhck, about v5 you can find more information here: https://github.com/twbs/bootstrap/projects/11

Currently with jQuery all of our components can be initialized separately, and it'll be the case in v5 too, since Bootstrap use data-api with event delegation we'll continue to provide to auto initialization for some of our components

@rpokrovskij
Copy link

I'm not so sure that web components suits well for Bootstrap:

How would you break input group + prepened + append to web components ?
Or form-group + input + invalid-feedback ?

E.g.

<div class="input-group">
                        <input type="text" class="form-control" id="sample-complex-id" aria-label="Amount (to the nearest dollar)">
                        <div class="input-group-append">
                            <button class="btn btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Actions</button>
                            <div class="dropdown-menu">
                                <a class="dropdown-item" href="#"  onClick="$('#sample-complex-id').val('')">Clear</a>
                            </div>
                        </div>
</div>
<div class="valid-feedback">Looks good!</div>
<div class="invalid-feedback">Invalid!</div>

Do you see how it could be wroted with custom-elements (that have shadow doms) ?

@bennypowers
Copy link

@rpokrovskij
Some variation of this:

<twb-input-group>
  <twb-input slot="input"></twb-input>
  <twb-dropdown id="sample-complex-id" slot="append">
    <twb-button slot="trigger">Actions</button>
    <twb-item>
      <a href="#" onclick="this.getRootNode().getElementById('sample-complex-id').reset()">Clear</a>
    </twb-item>
  </twb-dropdown>
  <output slot="valid-feedback">Looks good!</output>
  <output slot="invalid-feedback">Invalid!</output>
</twb-input-group>

@blikblum
Copy link

I'm not so sure that web components suits well for Bootstrap:

I share the same sentiment, at least partially, specially for layout only components.

The markup of a bootstrap web component library would not be much smaller, easier to write / learn than using div elements and classes. And also would come with a significant amount of code / runtime overhead.

The benefit i see is for instantiating javascript components, like modal, declaratively

BTW: i use Boostrap with web components (without shadow dom). The web components are for views (e.g., a page) or for sections of a view. The basic elements are still done with Bootstrap classes.

Do you see how it could be wroted with custom-elements (that have shadow doms) ?

Here is an actual implementation:
https://lit-element-bootstrap.dev/component/forms

See that the markup is not much smaller than using vanilla bootstrap

Also look at the amount of code required (of one element - bs-form-group):
https://github.com/nik-christou/lit-element-bootstrap/blob/master/packages/components/form/src/bs-form-group.js

@rpokrovskij
Copy link

rpokrovskij commented Feb 15, 2020

Some variation of this:

<twb-input-group>
  <twb-input slot="input"></twb-input>
  <twb-dropdown id="sample-complex-id" slot="append">

Thank you. Could you formulate what kind of advantage that bring? I see it this way: there are no place for Shadow Dom - it is plain mark up. Shadow Dom is usefull when you generate some DOM inside the custom element. But Bootsrap component's are not about code generation (and your sample code confirms this).

@rpokrovskij
Copy link

rpokrovskij commented Feb 15, 2020

BTW: i use Boostrap with web components (without shadow dom). The web components are for views (e.g., a page) or for sections of a view. The basic elements are still done with Bootstrap classes.

Could you share some knowledge about it?
Do you add the bootsrap.css to every your Custom Element's shadow DOM ?
How you pass popper into your web component that contains "popper enabled" code?
Do you use templates with some "bootsrap" html inisde?

@blikblum
Copy link

blikblum commented Feb 15, 2020

Do you add the bootsrap.css to every your Custom Element's shadow DOM ?

As pointed, i dont use shadow DOM, so styles are global

How you pass popper into your web component that contains "popper enabled" code?

Currently, i dont use tooltips.

Do you use templates with some "bootsrap" html inisde?

Yes.

Below is a view component

class DashboardView extends Component {
  resultRangeClick(e) {
    this.cultureResults.dayRange = e.detail.value
    this.cultureResults.fetch()
    this.requestUpdate()
  }

  render() {
    return html`
      ${pageHeader('Painel de Controle')}
      <div class="container-fluid mt--7">
        <div class="row mt-5">
          <div class="col mb-5 mb-xl-0">
            <div class="card shadow">
              <div class="card-header border-0">
                <div class="row align-items-center">
                  <div class="col">
                    <h3 class="mb-0">Resultados de Culturas</h3>
                  </div>
                  <div class="col text-right">
                    <menu-button
                      icon="calendar"
                      .value=${this.cultureResults.dayRange}
                      .items=${[
                        { name: 'Último Dia', value: 1 },
                        { name: 'Última Semana', value: 7 },
                        { name: 'Últimos 15 Dias', value: 15 },
                        { name: 'Últimos 30 Dias', value: 30 },
                        { name: 'Últimos 2 Meses', value: 60 },
                      ]}
                      @item-click=${this.resultRangeClick}
                    ></menu-button>
                  </div>
                </div>
              </div>
              <div class="table-responsive table-wrapped-column">
                <data-table
                  thead-light
                  .fields=${[
                    { title: 'Data / Hora', render: renderDateCell, styles: { width: '8%' } },
                    { title: 'Paciente', attr: 'patientname', styles: { width: '20%' } },
                    {
                      title: 'Material',
                      render: renderMaterialCell,
                      styles: { width: '5%' },
                    },
                    { title: 'Resultado', attr: 'result', styles: { width: '25%' } },
                    { title: 'Observações', attr: 'notes' },
                  ]}
                  .collection=${this.cultureResults}
                ></data-table>
              </div>
            </div>
          </div>
        </div>
      </div>
    `
  }
}

data-table is bs table component
menu-button is a component around bs dropdown:

class MenuButton extends Component {
  @property({ attribute: false })
  items = []

  @property({ type: String })
  icon

  @property({ type: String })
  caption

  @property({})
  value

  @event('click', '.dropdown-item')
  itemClick(e) {
    const item = e.selectorTarget.item
    this.dispatchEvent(new CustomEvent('item-click', { bubbles: true, detail: item }))
  }

  get iconClass() {
    return this.icon ? `fa fa-${this.icon}` : null
  }

  render() {
    const item = this.value !== undefined && this.items.find(item => item.value === this.value)
    const buttonCaption = item ? item.name : this.caption
    return html`
      <button type="button" class="btn btn-sm btn-secondary dropdown-toggle" data-toggle="dropdown">
        <span class=${this.iconClass}></span>
        ${buttonCaption}
      </button>
      <div class="dropdown-menu">
        ${this.items.map(item => {
          if (item.type === 'separator') {
            return html`
              <div class="dropdown-divider"></div>
            `
          } else {
            return html`
              <a class="dropdown-item" .item=${item}>${item.name}</a>
            `
          }
        })}
      </div>
    `
  }
}

pageHeader is a function that returns a lit-html template

@Izicomics
Copy link

My goal is to improve Izicomics my current platform to read online comis by myself, because i think that it does not adjust to current modern times, so at the moment i am learning everything basic from HTML and more, in the topic of bootstrap which of these components do you think would be useful for my project?

@bestguy
Copy link

bestguy commented Jul 17, 2020

Commenting to 👍 web components but vote to please not use shadow DOM and embed Bootstrap styles, or otherwise make it difficult to change theme via a global include of Bootstrap.

Many discussions of web components seem to end up in rabbit hole of assuming that means embedding styles, and then needing ad hoc ways to theme or customize. Bootstrap is so powerful because your interface is just the class names.

Markup generation seems handy though for some of the complex components that need many tags and JS. Random example:

<bs-modal header="Modal title" open>
  Modal body text goes here.
</bs-modal>

@rafalsk
Copy link

rafalsk commented Sep 7, 2020

We're having quite an issue with using Bootstrap within web-components. While the style-sheets import and render just fine within Shadow-DOM, the Boostrap scripts do not. We have tried multiple approaches to no avail. Including an attempt to simply import both the CSS and JavaScript file within the component. With Bootstrap 4 that wouldn't work. Shadow DOM is the only way for us to go since boundaries across components are one of our top priorities. ideas how to approach this?

@tones31
Copy link

tones31 commented Dec 11, 2020

Moving everything out of the shadow dom was the best choice I ever made for my applications. Shadow dom components only make sense if you are making a standalone component and not something to be used throughout your own application. You won't have any of your common styling, etc. It also is frustrating to get web components within shadow dom to work properly.

@nemethmik
Copy link

Your examples are excellent @blikblum exactly this was my conclusion, too: we can use light DOM custom elements in our application perfectly fine until we need slots Even slots can be worked around by passing slotted blocks as TemplateResult properties, With a light DOM custom element you cannot implement

<my-lightdomelement>
  <h1 slot=title>My Title</h1>
  <p style="...">This is the contents</p>
</my-lightdomelement>

Instead, we can do, however - not as elegant and clean but could work, fine especially with lit-html - like so:

<my-lightdomelement 
  .title=${html`<h1>My Title</h1>`} 
  .default=${html`<p style="...">This is the contents</p>`}>
</my-lightdomelement>

So, I see no problem with using light DOM custom elements extensively, especially with brilliant tools like Lit or Microsoft FAST ( which is brutally similar to Lit, BTW).

@GeoSot
Copy link
Member

GeoSot commented Oct 3, 2021

Guys, I am glad for digging this out. I think it would be great, if there is any volunteer here, where can start a MR, for the alert component, in order to guide the way, and make it easier for the team to decide over an existing example

@mdo
Copy link
Member

mdo commented Apr 13, 2022

I'd be fine to see some PRs here or suggestions to pull in some other repos already doing this for the community under our @twbs umbrella, but I'm closing this out as something we don't have time for ourselves unfortunately.

@mdo mdo closed this as not planned Won't fix, can't repro, duplicate, stale Apr 13, 2022
@ahelmi365
Copy link

Any idea how to use bootstrap inside the shadowDOM only without affecting elements outside the web component? I'm using openWC with Lit-Element

@bennypowers
Copy link

render() {
  return html`
    <link rel="stylesheet" href="/path/to/bootstrap.css">
    ...
  `;
}

or

import bootstrapStyles from '../shared-styles/bootstrap.css'; // use a build step to transform this into a CSSStyleSheet object

@customElement('blah-blah')
class BlahBlah extends LitElement {
  static styles = [bootstrapStyles];
  // ...
}

or

const bootstrapStyles = await fetch(bootstrapStylesUrl).then(r => r.text());
const bootstrapStyleSheet = new CSSStyleSheet();
bootstrapStyleSheet.replaceSync(bootstrapStyles);
myElement.shadowRoot.adoptedStyleSheets = [...myElement.shadowRoot.adoptedStyleSheets, bootStrapStyles];

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests