@@ -216,22 +216,12 @@ public class ResponsiveBuilder {
216
216
guard let breakpoint = currentBreakpoint else { return }
217
217
218
218
// Apply the breakpoint prefix to all pending classes
219
- let responsiveClasses = pendingClasses. map {
220
- // Handle duplication for flex-*, justify-*, items-*
221
- if $0. starts ( with: " flex- " ) || $0. starts ( with: " justify- " ) || $0. starts ( with: " items- " )
222
- || $0. starts ( with: " grid- " )
223
- {
224
- return " \( breakpoint. rawValue) \( $0) "
225
- } else if $0 == " flex " || $0 == " grid " {
226
- return " \( breakpoint. rawValue) \( $0) "
227
- } else {
228
- return " \( breakpoint. rawValue) \( $0) "
229
- }
230
- }
219
+ let responsiveClasses = pendingClasses. map { " \( breakpoint. rawValue) \( $0) " }
231
220
232
221
// Create a concrete wrapper that preserves Element conformance
233
222
let wrapped = AnyElement ( self . element)
234
- let styledModifier = StyleModifier ( content: wrapped, classes: responsiveClasses)
223
+
224
+ let styledModifier = StyleModifierWithDeduplication ( content: wrapped, classes: responsiveClasses)
235
225
self . element = ElementWrapper ( styledModifier)
236
226
237
227
// Clear pending classes for the next breakpoint
@@ -245,6 +235,61 @@ public class ResponsiveBuilder {
245
235
}
246
236
}
247
237
238
+ /// A smart style modifier that deduplicates redundant classes
239
+ struct StyleModifierWithDeduplication < T: HTML > : HTML {
240
+ private let content : T
241
+ private let classes : [ String ]
242
+
243
+ init ( content: T , classes: [ String ] ) {
244
+ self . content = content
245
+ self . classes = classes
246
+ }
247
+
248
+ /// Removes redundant modifier classes when the same property exists in base
249
+ private func filterRedundantModifierClasses( _ modifierClasses: [ String ] ) -> [ String ] {
250
+ // Get base classes by rendering the content first
251
+ let baseContent = content. render ( )
252
+ let baseClasses = extractClassesFromHTML ( baseContent)
253
+
254
+ return modifierClasses. filter { modifierClass in
255
+ guard let colonIndex = modifierClass. firstIndex ( of: " : " ) else {
256
+ return true // Not a modifier class, keep it
257
+ }
258
+
259
+ let baseClass = String ( modifierClass [ modifierClass. index ( after: colonIndex) ... ] )
260
+
261
+ // Remove redundant transition property declarations
262
+ if baseClass. hasPrefix ( " transition- " ) &&
263
+ !baseClass. hasPrefix ( " transition-duration " ) &&
264
+ !baseClass. hasPrefix ( " transition-delay " ) &&
265
+ !baseClass. hasPrefix ( " transition-timing " ) {
266
+ return !baseClasses. contains ( baseClass)
267
+ }
268
+
269
+ // Keep all other modifier classes
270
+ return true
271
+ }
272
+ }
273
+
274
+ /// Extracts classes from HTML class attribute
275
+ private func extractClassesFromHTML( _ html: String ) -> Set < String > {
276
+ let pattern = #"class="([^"]*)"#
277
+ guard let regex = try ? NSRegularExpression ( pattern: pattern) ,
278
+ let match = regex. firstMatch ( in: html, range: NSRange ( html. startIndex... , in: html) ) ,
279
+ let range = Range ( match. range ( at: 1 ) , in: html) else {
280
+ return Set ( )
281
+ }
282
+
283
+ let classString = String ( html [ range] )
284
+ return Set ( classString. split ( separator: " " ) . map ( String . init) )
285
+ }
286
+
287
+ var body : some HTML {
288
+ let filteredClasses = filterRedundantModifierClasses ( classes)
289
+ return content. addingClasses ( filteredClasses)
290
+ }
291
+ }
292
+
248
293
// Font styling methods
249
294
extension ResponsiveBuilder {
250
295
@discardableResult
0 commit comments