Widget overlapping only on iOS

Hi All,

I have been working on two widgets, one is a media player card based on the community shared ones, and one is basically generating the media player cards based on players under a group item using repeater.
The code for the player widget itself is here:

uid: Speaker_Card
tags: []
props:
  parameters:
    - description: Player Title
      label: Player Title
      name: playerTitle
      required: false
      type: TEXT
    - context: item
      label: Player Control Item
      name: playerControlItem
      required: true
      type: TEXT
    - context: item
      label: Artist Name Item
      name: artistItem
      required: true
      type: TEXT
    - context: item
      label: Track Name Item
      name: trackItem
      required: true
      type: TEXT
    - context: item
      label: Album Art Item
      name: albumArtItem
      required: false
      type: TEXT
    - context: item
      label: Track Progress Time Item
      name: trackProgressItem
      required: false
      type: TEXT
    - context: item
      label: Track Total Time Item
      name: trackTimeItem
      required: false
      type: TEXT
    - context: item
      label: Power Item
      name: powerItem
      required: false
      type: TEXT
    - context: item
      label: Multiroom Control Item
      name: multiroomControlItem
      required: false
      type: TEXT
    - context: item
      label: Volume Control Item
      name: volumeControlItem
      required: false
      type: TEXT
    - label: Force Dark Theme
      name: darkTheme
      required: false
      type: BOOLEAN
  parameterGroups: []
timestamp: May 30, 2023, 11:42:03 PM
component: f7-card
config:
  style:
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: 5px 5px 10px 1px rgba(0,0,0,0.1)
    height: "=props.playerTitle ? 200px : 160px"
    min-width: 350px
    width: 100%
  themeDark: =props.darkTheme
  title: =props.playerTitle
slots:
  default:
    - component: f7-card-content
      slots:
        default:
          - component: oh-image
            config:
              style:
                float: left
                height: 80px
                width: 80px
              url: "=props.albumArtItem && items[props.albumArtItem].state !== 'NULL' && items[props.albumArtItem].state !== 'UNDEF' ? 'items[props.albumArtItem].state' : 'https://media.istockphoto.com/id/164539209/de/vektor/beachten-sie.jpg?s=2048x2048&w=is&k=20&c=0hg7yBhG0-1R4ySp8wFcMDZVCVpUIoXBo3BuyxBncwM='"
          - component: f7-block
            config:
              style:
                display: flex
                flex-direction: column
            slots:
              default:
                - component: Label
                  config:
                    class:
                      - margin-top
                    style:
                      white-space: nowrap
                    text: =items[props.artistItem].state
                - component: Label
                  config:
                    style:
                      font-size: 20px
                      white-space: nowrap
                    text: =items[props.trackItem].state
                - component: Label
                  config:
                    style:
                      color: gray
                      font-size: 12px
                      height: 20px
                      margin-top: 2px
                    text: "=(props.trackProgressItem && props.trackTimeItem) ? Math.floor(Number.parseFloat(items[props.trackProgressItem].state) / 60) + ':' + ((Number.parseInt(items[props.trackProgressItem].state) % 60) < 10 ? ('0' + Number.parseInt(items[props.trackProgressItem].state) % 60) : (Number.parseInt(items[props.trackProgressItem].state) % 60)) + ' / ' + Math.floor(Number.parseFloat(items[props.trackTimeItem].state) / 60) + ':' + ((Number.parseInt(items[props.trackTimeItem].state) % 60) < 10 ? ('0' + Number.parseInt(items[props.trackTimeItem].state) % 60) : (Number.parseInt(items[props.trackTimeItem].state) % 60)) : ''"
                - component: oh-player-controls
                  config:
                    color: green
                    item: =props.playerControlItem
                    style:
                      margin: 1rem
                    visible: =!vars.showVolume
                - component: oh-slider
                  config:
                    color: blue
                    item: =props.volumeControlItem
                    style:
                      margin-top: 2rem
                    visible: =vars.showVolume === true
          - component: f7-block
            config:
              style:
                display: flex
                flex-direction: row-reverse
                justify-content: center
                position: absolute
                text-align: right
                top: 137px
                width: 112px
            slots:
              default:
                - component: oh-link
                  config:
                    action: variable
                    actionVariable: showVolume
                    actionVariableValue: =!(vars.showVolume === true)
                    color: "=(vars.showVolume) ? 'blue' : 'gray'"
                    iconF7: speaker_3_fill
                    iconSize: 18
                    round: true
                    style:
                      margin: 0 0.5em
                    visible: =props.volumeControlItem !== undefined
                - component: oh-link
                  config:
                    action: options
                    actionItem: =props.multiroomControlItem
                    color: gray
                    iconF7: music_house_fill
                    iconSize: 18
                    round: true
                    style:
                      margin: 0 0.5em
                    tooltip: =items[props.multiroomControlItem].displayState || items[props.multiroomControlItem].state
                    visible: =props.multiroomControlItem !== undefined
                - component: oh-link
                  config:
                    action: toggle
                    actionCommand: ON
                    actionCommandAlt: OFF
                    actionItem: =props.powerItem
                    iconF7: power
                    iconSize: 18
                    round: true
                    style:
                      color: var(--f7-card-text-color)
                      margin: 0 0.5em
                      opacity: "=(items[props.powerItem].state === 'ON') ? 1 : 0.3"
                    visible: =props.powerItem !== undefined

and the code for the repeater widget is here:

uid: Generator_Speaker
tags: []
props:
  parameters:
    - context: item
      description: Group Item to control
      label: Group Item
      name: grpItem
      required: false
      type: TEXT
  parameterGroups: []
timestamp: May 30, 2023, 7:44:13 PM
component: f7-block
config:
  style:
    display: flex
    flex-wrap: wrap
slots:
  default:
    - component: oh-repeater
      config:
        for: item
        fragment: true
        groupItem: =props.grpItem
        sourceType: itemsInGroup
      slots:
        default:
          - component: f7-block
            config:
              class:
                - no-padding
                - no-margin
              style:
                display: flex
            slots:
              default:
                - component: widget:Speaker_Card
                  config:
                    albumArtItem: =loop.item.name.split('_')[0] + '_' + loop.item.name.split('_')[1] + '_ImageUrl'
                    artistItem: =loop.item.name.split('_')[0] + '_' + loop.item.name.split('_')[1] + '_MediaArtist'
                    playerControlItem: =loop.item.name
                    playerTitle: =loop.item.label.split(' ')[0] + ' Player'
                    powerItem: =loop.item.name.split('_')[0] + '_' + loop.item.name.split('_')[1] + '_Stop'
                    trackItem: =loop.item.name.split('_')[0] + '_' + loop.item.name.split('_')[1] + '_Title'
                    trackProgressItem: =loop.item.name.split('_')[0] + '_' + loop.item.name.split('_')[1] + '_CurrTime'
                    trackTimeItem: =loop.item.name.split('_')[0] + '_' + loop.item.name.split('_')[1] + '_Duration'
                    volumeControlItem: =loop.item.name.split('_')[0] + '_' + loop.item.name.split('_')[1] + '_Volume'

The widget is working properly on desktops or Android phones as shown below:

But on iphone the title and other controls are overlapped as shown below:

Any ideas what is causing this?

The issue is less any one specific thing and more that the construction of the widget itself is awkward and prone to breaking when on different screen sizes. I assume then, that the iPhone screen is smaller than some minimum width which causes a rearrangement of the three major pieces (the image, the info/player block, and the other buttons block).

There’s no one single quick fix for this. A major re-write of the player widget is required to make it more responsive to screen size changes.

The issues that would need to be addressed appear to be:

  1. The image is set to float: left. The float style is useful if you want the contents of another container to flow around an element, it is not a viable substitute for proper relative positioning.
  2. The second block (with the links) is set to position: absolute and that position is specified with absolute pixels not some relative unit.

In short, when the screen width gets too small, the contents of the info/player block cannot flow around the floated image (mostly the fault of the player control, I suspect) and so the entire block is placed below the image instead. However, because the second block is set to absolute position, it does not move to accommodate the first block but instead remains in its place already below the image and the two blocks are rendered as overlapping.

Thanks alot!
Your comments guided me to the right approach… it moved things into rows/columns for better control and updated the points to be relative instead of absolute…
Thanks again