I'm not an expert in angular, bootstrap, or stripejs, but I just read the documentations.
Firstly, in order for bootstrap floating labels to work, it requires the <input>
and <label>
to be siblings, because the floating label will work because of :placeholder-shown
pseudo element. The problem here is that your mounted stripe elements aren't an input, they became an input wrapped by an iframe, so bootstrap floating labels won't work without some tweak.
Secondly, you mentioned the stripe API documentation that says that we can use { labels: 'floating' }
, and after trying this, it turns out that it only works for elements that are mounted as a pack (not sure what it's called), one of that elements is address
, and it works because the label is inside the iframe (I attach the proof on the Stackblitz link down below). And then, in order to use a label on a mounted element, we must use this method. Unfortunately, that method didn't make the label injected inside the iframe, it's still outside, so the floating labels from the appearance API won't work.
Despite being inside the iframe, thankfully, the inputs can still change the class outside the iframe (it could give StripeElement--focus
, StripeElement--complete
, and StripeElement--invalid
), so we could just imitate the behavior of the bootstrap floating labels and implement it to our custom code, by copying the bootstrap behavior.
The easiest solution I could think of is by adding this CSS:
...
/* reset the original behavior of the floating labels for #custom-inputs */
#custom-inputs .form-floating > .form-control:focus ~ label,
#custom-inputs .form-floating > .form-control:not(:placeholder-shown) ~ label {
opacity: 1;
transform: scale(1) translateY(0) translateX(0);
}
/* mimic the behavior of bootstrap floating labels for #custom-inputs */
#custom-inputs .form-floating > .form-control.StripeElement--focus ~ label,
#custom-inputs .form-floating > .form-control.StripeElement--complete ~ label,
#custom-inputs .form-floating > .form-control.StripeElement--invalid ~ label {
opacity: 0.8;
transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
}
#custom-inputs .StripeElement--focus {
color: #212529;
background-color: #fff;
border-color: #86b7fe;
outline: 0;
box-shadow: 0 0 0 0.25rem rgb(13 110 253 / 25%);
}
...
And then empty the placeholder (because we want to use the floating labels):
...
if (!this.cardNumber) {
this.cardNumber = this.elements.create('cardNumber', {
...this.emptyOptions,
placeholder: '',
});
this.cardNumber.mount('#floatingNumber');
}
if (!this.cardCvc) {
this.cardCvc = this.elements.create('cardCvc', {
...this.emptyOptions,
placeholder: '',
});
this.cardCvc.mount('#floatingCvc');
}
if (!this.cardExpiry) {
this.cardExpiry = this.elements.create('cardExpiry', {
...this.emptyOptions,
placeholder: '',
});
this.cardExpiry.mount('#floatingExpiry');
}
...
Here is the working altered Stackblitz