@@ -389,6 +389,8 @@ class Widget(DOMNode):
389389 "nocolor" : lambda widget : widget .app .no_color ,
390390 "first-of-type" : lambda widget : widget .first_of_type ,
391391 "last-of-type" : lambda widget : widget .last_of_type ,
392+ "first-child" : lambda widget : widget .first_child ,
393+ "last-child" : lambda widget : widget .last_child ,
392394 "odd" : lambda widget : widget .is_odd ,
393395 "even" : lambda widget : widget .is_even ,
394396 } # type: ignore[assignment]
@@ -500,6 +502,10 @@ def __init__(
500502 """Used to cache :first-of-type pseudoclass state."""
501503 self ._last_of_type : tuple [int , bool ] = (- 1 , False )
502504 """Used to cache :last-of-type pseudoclass state."""
505+ self ._first_child : tuple [int , bool ] = (- 1 , False )
506+ """Used to cache :first-child pseudoclass state."""
507+ self ._last_child : tuple [int , bool ] = (- 1 , False )
508+ """Used to cache :last-child pseudoclass state."""
503509 self ._odd : tuple [int , bool ] = (- 1 , False )
504510 """Used to cache :odd pseudoclass state."""
505511 self ._last_scroll_time = monotonic ()
@@ -852,6 +858,34 @@ def last_of_type(self) -> bool:
852858 return self ._last_of_type [1 ]
853859 return False
854860
861+ @property
862+ def first_child (self ) -> bool :
863+ """Is this the first widget in its siblings?"""
864+ parent = self .parent
865+ if parent is None :
866+ return True
867+ # This pseudo class only changes when the parent's nodes._updates changes
868+ if parent ._nodes ._updates == self ._first_child [0 ]:
869+ return self ._first_child [1 ]
870+ for node in parent ._nodes :
871+ self ._first_child = (parent ._nodes ._updates , node is self )
872+ return self ._first_child [1 ]
873+ return False
874+
875+ @property
876+ def last_child (self ) -> bool :
877+ """Is this the last widget in its siblings?"""
878+ parent = self .parent
879+ if parent is None :
880+ return True
881+ # This pseudo class only changes when the parent's nodes._updates changes
882+ if parent ._nodes ._updates == self ._last_child [0 ]:
883+ return self ._last_child [1 ]
884+ for node in reversed (parent ._nodes ):
885+ self ._last_child = (parent ._nodes ._updates , node is self )
886+ return self ._last_child [1 ]
887+ return False
888+
855889 @property
856890 def is_odd (self ) -> bool :
857891 """Is this widget at an oddly numbered position within its siblings?"""
@@ -1304,7 +1338,7 @@ def update_styles(children: list[DOMNode]) -> None:
13041338 """Update order related CSS"""
13051339 if before is not None or after is not None :
13061340 # If the new children aren't at the end.
1307- # we need to update both odd/even and first-of-type/last-of-type
1341+ # we need to update both odd/even, first-of-type/last-of-type and first-child/last-child
13081342 for child in children :
13091343 if child ._has_order_style or child ._has_odd_or_even :
13101344 child ._update_styles ()
0 commit comments